Hello community,
here is the log from the commit of package rubygem-web-console for
openSUSE:Factory checked in at 2016-11-17 12:42:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-web-console (Old)
and /work/SRC/openSUSE:Factory/.rubygem-web-console.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-web-console"
Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-web-console/rubygem-web-console.changes
2016-07-21 07:58:41.000000000 +0200
+++
/work/SRC/openSUSE:Factory/.rubygem-web-console.new/rubygem-web-console.changes
2016-11-17 12:43:00.000000000 +0100
@@ -1,0 +2,10 @@
+Sun Oct 30 05:46:40 UTC 2016 - [email protected]
+
+- updated to version 3.4.0
+ see installed CHANGELOG.markdown
+
+ ## 3.4.0
+
+ * [#205](https://github.com/rails/web-console/pull/205) Introduce
autocompletion ([@sh19910711])
+
+-------------------------------------------------------------------
Old:
----
web-console-3.3.1.gem
New:
----
web-console-3.4.0.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-web-console.spec ++++++
--- /var/tmp/diff_new_pack.ikLIBW/_old 2016-11-17 12:43:01.000000000 +0100
+++ /var/tmp/diff_new_pack.ikLIBW/_new 2016-11-17 12:43:01.000000000 +0100
@@ -24,7 +24,7 @@
#
Name: rubygem-web-console
-Version: 3.3.1
+Version: 3.4.0
Release: 0
%define mod_name web-console
%define mod_full_name %{mod_name}-%{version}
++++++ web-console-3.3.1.gem -> web-console-3.4.0.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/CHANGELOG.markdown new/CHANGELOG.markdown
--- old/CHANGELOG.markdown 2016-07-05 10:05:07.000000000 +0200
+++ new/CHANGELOG.markdown 2016-10-29 12:01:55.000000000 +0200
@@ -2,6 +2,10 @@
## master (unreleased)
+## 3.4.0
+
+* [#205](https://github.com/rails/web-console/pull/205) Introduce
autocompletion ([@sh19910711])
+
## 3.3.1
Drop support for Rails `4.2.0`.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/README.markdown new/README.markdown
--- old/README.markdown 2016-07-05 10:05:07.000000000 +0200
+++ new/README.markdown 2016-10-29 12:01:55.000000000 +0200
@@ -185,7 +185,7 @@
### Why I'm getting an undefined method `web_console`?
-Make sure you configuration lives in `config/environments/development.rb`.
+Make sure your configuration lives in `config/environments/development.rb`.
## Credits
Files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/context.rb
new/lib/web_console/context.rb
--- old/lib/web_console/context.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/lib/web_console/context.rb 2016-10-29 12:01:55.000000000 +0200
@@ -0,0 +1,43 @@
+module WebConsole
+ # A context lets you get object names related to the current session binding.
+ class Context
+ def initialize(binding)
+ @binding = binding
+ end
+
+ # Extracts entire objects which can be called by the current session unless
+ # the inputs is present.
+ #
+ # Otherwise, it extracts methods and constants of the object specified by
+ # the input.
+ def extract(input = nil)
+ input.present? ? local(input) : global
+ end
+
+ private
+
+ GLOBAL_OBJECTS = [
+ 'instance_variables',
+ 'local_variables',
+ 'methods',
+ 'class_variables',
+ 'Object.constants',
+ 'global_variables'
+ ]
+
+ def global
+ GLOBAL_OBJECTS.map { |cmd| eval(cmd) }
+ end
+
+ def local(input)
+ [
+ eval("#{input}.methods").map { |m| "#{input}.#{m}" },
+ eval("#{input}.constants").map { |c| "#{input}::#{c}" },
+ ]
+ end
+
+ def eval(cmd)
+ @binding.eval(cmd) rescue []
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/locales/en.yml
new/lib/web_console/locales/en.yml
--- old/lib/web_console/locales/en.yml 2016-07-05 10:05:07.000000000 +0200
+++ new/lib/web_console/locales/en.yml 2016-10-29 12:01:55.000000000 +0200
@@ -1,7 +1,7 @@
en:
errors:
unavailable_session: |
- Session %{id} is is no longer available in memory.
+ Session %{id} is no longer available in memory.
If you happen to run on a multi-process server (like Unicorn or Puma)
the process
this request hit doesn't store %{id} in memory. Consider turning the
number of
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/middleware.rb
new/lib/web_console/middleware.rb
--- old/lib/web_console/middleware.rb 2016-07-05 10:05:07.000000000 +0200
+++ new/lib/web_console/middleware.rb 2016-10-29 12:01:55.000000000 +0200
@@ -103,7 +103,11 @@
def update_repl_session(id, request)
json_response_with_session(id, request) do |session|
- { output: session.eval(request.params[:input]) }
+ if input = request.params[:input]
+ { output: session.eval(input) }
+ elsif input = request.params[:context]
+ { context: session.context(input) }
+ end
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/railtie.rb
new/lib/web_console/railtie.rb
--- old/lib/web_console/railtie.rb 2016-07-05 10:05:07.000000000 +0200
+++ new/lib/web_console/railtie.rb 2016-10-29 12:01:55.000000000 +0200
@@ -40,6 +40,9 @@
if mount_point = config.web_console.mount_point
Middleware.mount_point = mount_point.chomp('/')
end
+ if root = Rails.application.config.relative_url_root
+ Middleware.mount_point = File.join(root, Middleware.mount_point)
+ end
end
initializer 'web_console.template_paths' do
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/session.rb
new/lib/web_console/session.rb
--- old/lib/web_console/session.rb 2016-07-05 10:05:07.000000000 +0200
+++ new/lib/web_console/session.rb 2016-10-29 12:01:55.000000000 +0200
@@ -43,7 +43,7 @@
def initialize(bindings)
@id = SecureRandom.hex(16)
@bindings = bindings
- @evaluator = Evaluator.new(bindings.first)
+ @evaluator = Evaluator.new(@current_binding = bindings.first)
store_into_memory
end
@@ -59,7 +59,12 @@
#
# Returns nothing.
def switch_binding_to(index)
- @evaluator = Evaluator.new(@bindings[index.to_i])
+ @evaluator = Evaluator.new(@current_binding = @bindings[index.to_i])
+ end
+
+ # Returns context of the current binding
+ def context(objpath)
+ Context.new(@current_binding).extract(objpath)
end
private
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/tasks/templates.rake
new/lib/web_console/tasks/templates.rake
--- old/lib/web_console/tasks/templates.rake 1970-01-01 01:00:00.000000000
+0100
+++ new/lib/web_console/tasks/templates.rake 2016-10-29 12:01:55.000000000
+0200
@@ -0,0 +1,54 @@
+namespace :templates do
+ desc 'Run tests for templates'
+ task test: [ :daemonize, :npm, :rackup, :wait, :mocha, :kill, :exit ]
+ task serve: [ :npm, :rackup ]
+
+ workdir = Pathname(EXPANDED_CWD).join('test/templates')
+ pid = Pathname(Dir.tmpdir).join("web_console_test.pid")
+ runner = URI.parse("http://#{ENV['IP'] || '127.0.0.1'}:#{ENV['PORT'] ||
29292}/html/test_runner.html")
+ rackup = "rackup --host #{runner.host} --port #{runner.port}"
+ result = nil
+ browser = 'phantomjs'
+
+ def need_to_wait?(uri)
+ Net::HTTP.start(uri.host, uri.port) { |http| http.get(uri.path) }
+ rescue Errno::ECONNREFUSED
+ retry if yield
+ end
+
+ task :daemonize do
+ rackup += " -D --pid #{pid}"
+ end
+
+ task :npm => [ :phantomjs ] do
+ Dir.chdir(workdir) { system 'npm install --silent' }
+ end
+
+ task :phantomjs do
+ unless system("which #{browser} >/dev/null")
+ browser = './node_modules/.bin/phantomjs'
+ Dir.chdir(workdir) { system("test -f #{browser} || npm install --silent
phantomjs-prebuilt") }
+ end
+ end
+
+ task :rackup do
+ Dir.chdir(workdir) { system rackup }
+ end
+
+ task :wait do
+ cnt = 0
+ need_to_wait?(runner) { sleep 1; cnt += 1; cnt < 5 }
+ end
+
+ task :mocha do
+ Dir.chdir(workdir) { result = system("#{browser}
./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js #{runner} dot") }
+ end
+
+ task :kill do
+ system "kill #{File.read pid}"
+ end
+
+ task :exit do
+ exit result
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/tasks/test_templates.rake
new/lib/web_console/tasks/test_templates.rake
--- old/lib/web_console/tasks/test_templates.rake 2016-07-05
10:05:07.000000000 +0200
+++ new/lib/web_console/tasks/test_templates.rake 1970-01-01
01:00:00.000000000 +0100
@@ -1,50 +0,0 @@
-namespace :test do
- desc "Run tests for templates"
- task templates: "templates:all"
-
- namespace :templates do
- task all: [ :daemonize, :npm, :rackup, :wait, :mocha, :kill, :exit ]
- task serve: [ :npm, :rackup ]
-
- work_dir = Pathname(EXPANDED_CWD).join("test/templates")
- pid_file =
Pathname(Dir.tmpdir).join("web_console.#{SecureRandom.uuid}.pid")
- runner_uri = URI.parse("http://localhost:29292/html/spec_runner.html")
- rackup_opts = "-p #{runner_uri.port}"
- test_result = nil
-
- def need_to_wait?(uri)
- Net::HTTP.start(uri.host, uri.port) { |http| http.get(uri.path) }
- rescue Errno::ECONNREFUSED
- retry if yield
- end
-
- task :daemonize do
- rackup_opts += " -D -P #{pid_file}"
- end
-
- task :npm do
- Dir.chdir(work_dir) { system "npm install --silent" }
- end
-
- task :rackup do
- Dir.chdir(work_dir) { system "bundle exec rackup #{rackup_opts}" }
- end
-
- task :wait do
- cnt = 0
- need_to_wait?(runner_uri) { sleep 1; cnt += 1; cnt < 5 }
- end
-
- task :mocha do
- Dir.chdir(work_dir) { test_result = system("$(npm bin)/mocha-phantomjs
#{runner_uri}") }
- end
-
- task :kill do
- system "kill #{File.read pid_file}"
- end
-
- task :exit do
- exit test_result
- end
- end
-end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/templates/console.js.erb
new/lib/web_console/templates/console.js.erb
--- old/lib/web_console/templates/console.js.erb 2016-07-05
10:05:07.000000000 +0200
+++ new/lib/web_console/templates/console.js.erb 2016-10-29
12:01:55.000000000 +0200
@@ -44,6 +44,212 @@
}
}
+function Autocomplete(_words, prefix) {
+ this.words = prepareWords(_words);
+ this.current = -1;
+ this.left = 0; // [left, right)
+ this.right = this.words.length;
+ this.confirmed = false;
+
+ function createSpan(label, className) {
+ var el = document.createElement('span');
+ addClass(el, className);
+ el.innerText = label;
+ return el;
+ }
+
+ function prepareWords(words) {
+ // convert into an object with priority and element
+ var res = new Array(words.length);
+ for (var i = 0, ind = 0; i < words.length; ++i) {
+ res[i] = new Array(words[i].length);
+ for (var j = 0; j < words[i].length; ++j) {
+ res[i][j] = {
+ word: words[i][j],
+ priority: i,
+ element: createSpan(words[i][j], 'trimmed keyword')
+ };
+ }
+ }
+ // flatten and sort by alphabetical order to refine incrementally
+ res = flatten(res);
+ res.sort(function(a, b) { return a.word == b.word ? 0 : (a.word < b.word ?
-1 : 1); });
+ for (var i = 0; i < res.length; ++i) res[i].element.dataset.index = i;
+ return res;
+ }
+
+ this.view = document.createElement('pre');
+ addClass(this.view, 'auto-complete console-message');
+ this.view.appendChild(this.prefix = createSpan('...', 'trimmed keyword'));
+ this.view.appendChild(this.stage = document.createElement('span'));
+ this.elements = this.stage.children;
+ this.view.appendChild(this.suffix = createSpan('...', 'trimmed keyword'));
+
+ this.refine(prefix || '');
+}
+
+Autocomplete.prototype.getSelectedWord = function() {
+ return this.lastSelected && this.lastSelected.innerText;
+};
+
+Autocomplete.prototype.onFinished = function(callback) {
+ this.onFinishedCallback = callback;
+ if (this.confirmed) callback(this.confirmed);
+};
+
+Autocomplete.prototype.onKeyDown = function(ev) {
+ var self = this;
+ if (!this.elements.length) return;
+
+ function move(nextCurrent) {
+ if (self.lastSelected) removeClass(self.lastSelected, 'selected');
+ addClass(self.lastSelected = self.elements[nextCurrent], 'selected');
+ self.trim(self.current, true);
+ self.trim(nextCurrent, false);
+ self.current = nextCurrent;
+ }
+
+ switch (ev.keyCode) {
+ case 69:
+ if (ev.ctrlKey) {
+ move(this.current + 1 >= this.elements.length ? 0 : this.current + 1);
+ return true;
+ }
+ return false;
+ case 9: // Tab
+ if (ev.shiftKey) { // move back
+ move(this.current - 1 < 0 ? this.elements.length - 1 : this.current -
1);
+ } else { // move next
+ move(this.current + 1 >= this.elements.length ? 0 : this.current + 1);
+ }
+ return true;
+ case 13: // Enter
+ this.finish();
+ return true;
+ case 27: // Esc
+ this.cancel();
+ return true;
+ case 37: case 38: case 39: case 40: // disable using arrow keys on
completing
+ return true;
+ }
+
+ return false;
+};
+
+Autocomplete.prototype.trim = function(from, needToTrim) {
+ var self = this;
+ var num = 5;
+
+ if (this.elements.length > num) {
+ (0 < from ? removeClass : addClass)(this.prefix, 'trimmed');
+ (from + num < this.elements.length ? removeClass : addClass)(this.suffix,
'trimmed');
+ } else {
+ addClass(this.prefix, 'trimmed');
+ addClass(this.suffix, 'trimmed');
+ }
+
+ function iterate(x) {
+ for (var i = 0; i < num; ++i, ++x) if (0 <= x && x < self.elements.length)
{
+ toggleClass(self.elements[x], 'trimmed');
+ }
+ }
+
+ var toggleClass = needToTrim ? addClass : removeClass;
+ if (from < 0) {
+ iterate(0);
+ } else if (from + num - 1 >= this.elements.length) {
+ iterate(this.elements.length - num);
+ } else {
+ iterate(from);
+ }
+};
+
+Autocomplete.prototype.refine = function(prefix) {
+ if (this.confirmed) return;
+ var inc = !this.prev || (prefix.length >= this.prev.length);
+ this.prev = prefix;
+ var self = this;
+
+ function remove(parent, child) {
+ if (parent == child.parentNode) parent.removeChild(child);
+ }
+
+ function toggle(el) {
+ return inc ? remove(self.stage, el) : self.stage.appendChild(el);
+ }
+
+ function startsWith(str, prefix) {
+ return !prefix || str.substr(0, prefix.length) === prefix;
+ }
+
+ function moveRight(l, r) {
+ while (l < r && inc !== startsWith(self.words[l].word, prefix))
toggle(self.words[l++].element);
+ return l;
+ }
+
+ function moveLeft(l, r) {
+ while (l < r - 1 && inc !== startsWith(self.words[r-1].word, prefix))
toggle(self.words[--r].element);
+ return r;
+ }
+
+ self.trim(self.current, true); // reset trimming
+
+ // Refine the range of words having same prefix
+ if (inc) {
+ self.left = moveRight(self.left, self.right);
+ self.right = moveLeft(self.left, self.right);
+ } else {
+ self.left = moveLeft(-1, self.left);
+ self.right = moveRight(self.right, self.words.length);
+ }
+
+ // Render elements with sorting by scope groups
+ var words = this.words.slice(this.left, this.right);
+ words.sort(function(a, b) { return a.priority == b.priority ? (a.word <
b.word ? -1 : 1) : (a.priority < b.priority ? -1 : 1); });
+ removeAllChildren(this.elements);
+ for (var i = 0; i < words.length; ++i) {
+ this.stage.appendChild(words[i].element);
+ }
+
+ // Keep a previous selected element if the refined range includes the element
+ if (this.lastSelected && this.left <= this.lastSelected.dataset.index &&
this.lastSelected.dataset.index < this.right) {
+ this.current = Array.prototype.indexOf.call(this.elements,
this.lastSelected);
+ this.trim(this.current, false);
+ } else {
+ if (this.lastSelected) removeClass(this.lastSelected, 'selected');
+ this.lastSelected = null;
+ this.current = -1;
+ this.trim(0, false);
+ }
+
+ if (self.left + 1 == self.right) {
+ self.current = 0;
+ self.finish();
+ } else if (self.left == self.right) {
+ self.cancel();
+ }
+};
+
+Autocomplete.prototype.finish = function() {
+ if (0 <= this.current && this.current < this.elements.length) {
+ this.confirmed = this.elements[this.current].innerText;
+ if (this.onFinishedCallback) this.onFinishedCallback(this.confirmed);
+ this.removeView();
+ } else {
+ this.cancel();
+ }
+};
+
+Autocomplete.prototype.cancel = function() {
+ if (this.onFinishedCallback) this.onFinishedCallback();
+ this.removeView();
+};
+
+Autocomplete.prototype.removeView = function() {
+ if (this.view.parentNode) this.view.parentNode.removeChild(this.view);
+ removeAllChildren(this.view);
+}
+
// HTML strings for dynamic elements.
var consoleInnerHtml = <%= render_inlined_string '_inner_console_markup.html'
%>;
var promptBoxHtml = <%= render_inlined_string '_prompt_box_markup.html' %>;
@@ -62,6 +268,7 @@
this.prompt = getConfig('promptLabel', ' >>');
this.mountPoint = getConfig('mountPoint');
this.sessionId = getConfig('sessionId');
+ this.autocomplete = false;
}
REPLConsole.prototype.getSessionUrl = function(path) {
@@ -73,6 +280,16 @@
return parts.join('/').replace(/([^:]\/)\/+/g, '$1');
};
+REPLConsole.prototype.contextRequest = function(keyword, callback) {
+ putRequest(this.getSessionUrl(), 'context=' + getContext(keyword),
function(xhr) {
+ if (xhr.status == 200) {
+ callback(null, JSON.parse(xhr.responseText));
+ } else {
+ callback(xhr.statusText);
+ }
+ });
+};
+
REPLConsole.prototype.commandHandle = function(line, callback) {
var self = this;
var params = 'input=' + encodeURIComponent(line);
@@ -92,7 +309,7 @@
function getErrorText(xhr) {
if (!xhr.status) {
- return "<%= t 'errors.connection_refused' %>";
+ return "Connection Refused";
} else {
return xhr.status + ' ' + xhr.statusText;
}
@@ -199,6 +416,7 @@
this.outer = consoleOuter;
this.inner = findChild(this.outer, 'console-inner');
this.clipboard = findChild(container, 'clipboard');
+ this.suggestWait = 1500;
this.newPromptBox();
this.insertCss();
@@ -267,10 +485,63 @@
this.setInput(this._input, -1);
};
+REPLConsole.prototype.getSuggestion = function(keyword) {
+ var self = this;
+
+ function show(found) {
+ if (!found) return;
+ var hint = self.promptDisplay.childNodes[1];
+ hint.className = 'console-hint';
+ hint.dataset.keyword = found;
+ hint.innerText = found.substr(self.suggestKeyword.length);
+ // clear hinting information after timeout in a few time
+ if (self.suggestTimeout) clearTimeout(self.suggestTimeout);
+ self.suggestTimeout = setTimeout(function() { self.renderInput() },
self.suggestWait);
+ }
+
+ function find(context) {
+ var k = self.suggestKeyword;
+ for (var i = 0; i < context.length; ++i) if (context[i].substr(0,
k.length) === k) {
+ if (context[i] === k) return;
+ return context[i];
+ }
+ }
+
+ function request(keyword, callback) {
+ self.contextRequest(keyword, function(err, res) {
+ if (err) throw new Error(err);
+ var c = flatten(res['context']);
+ c.sort();
+ callback(c);
+ });
+ }
+
+ self.suggestKeyword = keyword;
+ var input = getContext(keyword);
+ if (keyword.length - input.length < 3) return;
+
+ if (self.suggestInput !== input) {
+ self.suggestInput = input;
+ request(keyword, function(c) {
+ show(find(self.suggestContext = c));
+ });
+ } else if (self.suggestContext) {
+ show(find(self.suggestContext));
+ }
+};
+
+REPLConsole.prototype.getHintKeyword = function() {
+ var hint = this.promptDisplay.childNodes[1];
+ return hint.className === 'console-hint' && hint.dataset.keyword;
+};
+
REPLConsole.prototype.setInput = function(input, caretPos) {
+ if (input == null) return; // keep value if input is undefined
this._caretPos = caretPos === undefined ? input.length : caretPos;
this._input = input;
+ if (this.autocomplete) this.autocomplete.refine(this.getCurrentWord());
this.renderInput();
+ if (!this.autocomplete && input.length == this._caretPos)
this.getSuggestion(this.getCurrentWord());
};
/**
@@ -292,9 +563,8 @@
// Clear the current input.
removeAllChildren(this.promptDisplay);
- var promptCursor = document.createElement('span');
- promptCursor.className = "console-cursor";
var before, current, after;
+ var center = document.createElement('span');
if (this._caretPos < 0) {
before = this._input;
@@ -310,9 +580,12 @@
}
this.promptDisplay.appendChild(document.createTextNode(before));
- promptCursor.appendChild(document.createTextNode(current));
- this.promptDisplay.appendChild(promptCursor);
+ this.promptDisplay.appendChild(center);
this.promptDisplay.appendChild(document.createTextNode(after));
+
+ var hint = this.autocomplete && this.autocomplete.getSelectedWord();
+ addClass(center, hint ? 'console-hint' : 'console-cursor');
+ center.appendChild(document.createTextNode(hint ?
hint.substr(this.getCurrentWord().length) : current));
};
REPLConsole.prototype.writeOutput = function(output) {
@@ -340,6 +613,30 @@
this.commandHandle(input);
};
+REPLConsole.prototype.onTabKey = function() {
+ var self = this;
+
+ var hintKeyword;
+ if (hintKeyword = self.getHintKeyword()) {
+ self.swapCurrentWord(hintKeyword);
+ return;
+ }
+
+ if (self.autocomplete) return;
+ self.autocomplete = new Autocomplete([]);
+
+ self.contextRequest(self.getCurrentWord(), function(err, obj) {
+ if (err) return self.autocomplete = false;
+ self.autocomplete = new Autocomplete(obj['context'],
self.getCurrentWord());
+ self.inner.appendChild(self.autocomplete.view);
+ self.autocomplete.onFinished(function(word) {
+ self.swapCurrentWord(word);
+ self.autocomplete = false;
+ });
+ self.scrollToBottom();
+ });
+};
+
REPLConsole.prototype.onNavigateHistory = function(offset) {
var command = this.commandStorage.navigate(offset) || "";
this.setInput(command);
@@ -349,7 +646,26 @@
* Handle control keys like up, down, left, right.
*/
REPLConsole.prototype.onKeyDown = function(ev) {
+ if (this.autocomplete && this.autocomplete.onKeyDown(ev)) {
+ this.renderInput();
+ ev.preventDefault();
+ ev.stopPropagation();
+ return;
+ }
+
switch (ev.keyCode) {
+ case 69:
+ // Ctrl-E
+ if (ev.ctrlKey) {
+ this.onTabKey();
+ ev.preventDefault();
+ }
+ break;
+ case 9:
+ // Tab
+ this.onTabKey();
+ ev.preventDefault();
+ break;
case 13:
// Enter key
this.onEnterKey();
@@ -432,6 +748,11 @@
var before = this._input.substring(0, caretPos);
var after = this._input.substring(this._caretPos, this._input.length);
this.setInput(before + after, caretPos);
+
+ if (!this._input) {
+ this.autocomplete && this.autocomplete.cancel();
+ this.autocomplete = false;
+ }
}
};
@@ -444,6 +765,31 @@
this.setInput(before + char + after, this._caretPos + 1);
};
+REPLConsole.prototype.swapCurrentWord = function(next) {
+ function right(s, pos) {
+ var x = s.indexOf(' ', pos);
+ return x === -1 ? s.length : x;
+ }
+
+ function swap(s, pos) {
+ return s.substr(0, s.lastIndexOf(' ', pos) + 1) + next + s.substr(right(s,
pos))
+ }
+
+ if (!next) return;
+ var swapped = swap(this._input, this._caretPos);
+ this.setInput(swapped, this._caretPos + swapped.length - this._input.length);
+};
+
+REPLConsole.prototype.getCurrentWord = function() {
+ return (function(s, pos) {
+ var left = s.lastIndexOf(' ', pos);
+ if (left === -1) left = 0;
+ var right = s.indexOf(' ', pos)
+ if (right === -1) right = s.length - 1;
+ return s.substr(left, right - left + 1).replace(/^\s+|\s+$/g,'');
+ })(this._input, this._caretPos);
+};
+
REPLConsole.prototype.scrollToBottom = function() {
this.outer.scrollTop = this.outer.scrollHeight;
};
@@ -514,7 +860,7 @@
for (var i = 0; i < el.length; ++ i) {
addClass(el[i], className);
}
- } else {
+ } else if (!hasClass(el, className)) {
el.className += " " + className;
}
}
@@ -562,3 +908,15 @@
} else {
window.REPLConsole = REPLConsole;
}
+
+// Split string by module operators of ruby
+function getContext(s) {
+ var methodOp = s.lastIndexOf('.');
+ var moduleOp = s.lastIndexOf('::');
+ var x = methodOp > moduleOp ? methodOp : moduleOp;
+ return x !== -1 ? s.substr(0, x) : '';
+}
+
+function flatten(arrays) {
+ return Array.prototype.concat.apply([], arrays);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/templates/style.css.erb
new/lib/web_console/templates/style.css.erb
--- old/lib/web_console/templates/style.css.erb 2016-07-05 10:05:07.000000000
+0200
+++ new/lib/web_console/templates/style.css.erb 2016-10-29 12:01:55.000000000
+0200
@@ -11,6 +11,12 @@
.console .console-prompt-box { color: #FFF; }
.console .console-message { color: #1AD027; margin: 0; border: 0; white-space:
pre-wrap; background-color: #333; padding: 0; }
.console .console-message.error-message { color: #fc9; }
+.console .console-message.auto-complete { word-break: break-all; }
+.console .console-message.auto-complete .keyword { margin-right: 11px; }
+.console .console-message.auto-complete .keyword.selected { background: #FFF;
color: #000; }
+.console .console-message.auto-complete .hidden { display: none; }
+.console .console-message.auto-complete .trimmed { display: none; }
+.console .console-hint { color: #096; }
.console .console-focus .console-cursor { background: #FEFEFE; color: #333;
font-weight: bold; }
.console .resizer { background: #333; width: 100%; height: 4px; cursor:
ns-resize; }
.console .console-actions { padding-right: 3px; }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/testing/fake_middleware.rb
new/lib/web_console/testing/fake_middleware.rb
--- old/lib/web_console/testing/fake_middleware.rb 2016-07-05
10:05:07.000000000 +0200
+++ new/lib/web_console/testing/fake_middleware.rb 2016-10-29
12:01:55.000000000 +0200
@@ -21,7 +21,7 @@
end
def view
- @view ||= View.new(@view_path)
+ @view = View.new(@view_path)
end
private
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/version.rb
new/lib/web_console/version.rb
--- old/lib/web_console/version.rb 2016-07-05 10:05:07.000000000 +0200
+++ new/lib/web_console/version.rb 2016-10-29 12:01:55.000000000 +0200
@@ -1,3 +1,3 @@
module WebConsole
- VERSION = '3.3.1'
+ VERSION = '3.4.0'
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console/view.rb new/lib/web_console/view.rb
--- old/lib/web_console/view.rb 2016-07-05 10:05:07.000000000 +0200
+++ new/lib/web_console/view.rb 2016-10-29 12:01:55.000000000 +0200
@@ -31,7 +31,11 @@
#
# Helps to keep the Rails logs clean during errors.
def render(*)
- WebConsole.logger.silence { super }
+ if logger = WebConsole.logger and logger.respond_to?(:silence)
+ WebConsole.logger.silence { super }
+ else
+ super
+ end
end
# Override method for ActionView::Helpers::TranslationHelper#t.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/web_console.rb new/lib/web_console.rb
--- old/lib/web_console.rb 2016-07-05 10:05:07.000000000 +0200
+++ new/lib/web_console.rb 2016-10-29 12:01:55.000000000 +0200
@@ -15,6 +15,7 @@
autoload :Whitelist
autoload :Template
autoload :Middleware
+ autoload :Context
autoload_at 'web_console/errors' do
autoload :Error
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2016-07-05 10:05:07.000000000 +0200
+++ new/metadata 2016-10-29 12:01:55.000000000 +0200
@@ -1,7 +1,7 @@
--- !ruby/object:Gem::Specification
name: web-console
version: !ruby/object:Gem::Version
- version: 3.3.1
+ version: 3.4.0
platform: ruby
authors:
- Charlie Somerville
@@ -11,7 +11,7 @@
autorequire:
bindir: bin
cert_chain: []
-date: 2016-07-05 00:00:00.000000000 Z
+date: 2016-10-29 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: railties
@@ -85,6 +85,7 @@
- Rakefile
- lib/web-console.rb
- lib/web_console.rb
+- lib/web_console/context.rb
- lib/web_console/errors.rb
- lib/web_console/evaluator.rb
- lib/web_console/exception_mapper.rb
@@ -99,7 +100,7 @@
- lib/web_console/response.rb
- lib/web_console/session.rb
- lib/web_console/tasks/extensions.rake
-- lib/web_console/tasks/test_templates.rake
+- lib/web_console/tasks/templates.rake
- lib/web_console/template.rb
- lib/web_console/templates/_inner_console_markup.html.erb
- lib/web_console/templates/_markup.html.erb