Faster Emacs Window Switching Inside a Frame

I needed something to speed up my work when I have few windows, I was looking at switch-window.el, but it kind of silly that the content of the window disappear. So I’ve written this simple 2 functions which act similar to my other sollution Faster buffer bookmarking in Emacs. But you mark window to which you want to jump.

It set Keyboard C-c C-0 for mark and C-C C-<1-9> to jump.

(setq window-alist '())

(defun mark-window (number)
  "Give current window a number to select it later using `switch-to-number-window'"
  (interactive "nNumber Your Window: ")
  (let ((pair (assoc number window-alist))
        (window (get-buffer-window)))
    (if pair
        (setf (cdr pair) window)
      (add-to-list 'window-alist (cons number window)))))

(defun switch-to-number-window (number)
  "Jump to the window  marked with a `mark-window' function"
  (interactive "nNumber Your Window: ")
  (let ((pair (assoc number window-alist)))
    (if pair
        (select-window (cdr pair))
      (message "Invalid Number"))))

;; create 9 keyboard shortcuts using closure (forced using lexical-let)
(dolist (i (map #'1+ (range 9)))
    (global-set-key (read-kbd-macro (concat "C-c C-"
                                            (number-to-string i)))
                    (lexical-let ((i i))
                      (lambda ()
                        (interactive)
                        (switch-to-number-window i)))))

(global-set-key (kbd "C-c C-0") 'mark-window)

It use range helper function (same as in python)

(defun range (n &optional list)
  "function return list of numbers from 1 to n"
  (if (eq n 0)
      list
    (let ((n (- n 1)))
      (range n (cons n list)))))

EDIT: I just found windmove commands which are much better.

, ,

Leave a comment

How To Add Translation Feature To Twitter

I don’t know about you but, I follow few peple that sometimes tweet in different language then English. And English is the only foreign language I know. So if I want to see what they tweet about sometimes I use google translate to read the content.

I always thought that it will be nice feature for Twitter to have translate button, so I created one, using WebSockets and Ruby.

So how I did this, first I downloaded websockets ruby library from github web-socket-ruby, I already had translation script written in Ruby that use google translate, so I added websocket server to it.

#!/usr/bin/ruby

require 'net/http'
require 'uri'
require 'optparse'
require 'json'
require 'socket'
require 'web_socket'

class NotConnectedException < Exception
end

def server(port, domains)
  server = WebSocketServer.new(
    :accepted_domains => domains,
    :port => port)
  puts("Server is running at port %d" % server.port)
  server.run() do |ws|
    puts("Connection accepted")
    puts("Path: #{ws.path}, Origin: #{ws.origin}")
    if ws.path == "/translate"
      ws.handshake()
      while data = ws.receive()
        printf("Received: %p\n", data)
        data = JSON.parse(data)
        response = translate(data['text'], nil, data['to_lang']).join("\n")
        ws.send(response)
        printf("Sent: %p\n", response)
      end
    else
      ws.handshake("404 Not Found")
    end
    puts("Connection closed")
  end
end

def escape(o)
  o.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
    '%' + $1.unpack('H2' * $1.size).join('%').upcase
  }.tr(' ', '+')
end

def translate(text, from=nil, to=nil, cookie=nil)
  url = URI.parse("http://translate.google.com/translate_a/t")
  query = "?hl=en&client=t&text=#{escape(text)}&multires=1&otf=1&pc=0&sc=1&ie=UTF-8&oe=UTF-8"
  query += "&sl=" + (from ? from : 'auto')
  if to
    query += "&tl=#{to}"
  end
  begin
    http = Net::HTTP.new(url.host)
    res = http.get(url.path + query)
    res.response['content-type'] =~ /charset=(.*)/
    charset = $1
    JSON.parse(res.body.gsub(/,{2,}/, ',').gsub(/,\]/, ']'))[0].map {|i|
      # google sometimes put spaces around numbers
      i[0].gsub(/\{ *([0-9]) *\}/, '{\1}')
    }
  rescue NoMethodError
    raise NotConnectedException
  rescue SocketError
    raise NotConnectedException
  rescue 
  end  
 
end

def msg(str, type='info')
  system("zenity --#{type} --title='translation' --text='#{str}'")
end

params = ARGV.getopts('i:o:gcsp:')

def error(msg, gui=false)
  if gui
    msg(msg, 'error')
  else
    puts msg
  end
end

def usage()
  puts "usage:"
  puts "translate [-i <INPUT LANG>] -o <OUTPUT LANG> [MORE OPTIONS]"
  puts "-g - show zenity dialog"
  puts "-c - get input from clipboard"
  puts "-s - run as server"
  puts "-p - server port"
end

begin
  if params['s']
     begin
       server(params['p'] ? params['p'].to_i() : 8080, 'twitter.com')
     rescue Interrupt
       puts "Server Exit"
     end
  else
    if params['c']
      input = `xclip -o -sel clip`
    else
      input = ARGF.read()
    end
    translation = translate(input, params['i'], params['o'])
    if params['g']
      msg(translation.join(". "))
    else
      translation.each{|sentence|
        if sentence != ''
          puts sentence
        end
      }
    end
  end
rescue JSON::ParserError => e
  error("Response Error: " + e.message, params['g'])
rescue NotConnectedException
  error("sorry but it seems that your internet connection is down", params['g'])
end

Then I created this bookmarklet. (I notice that profile page use jQuery.noConflict() so I can’t access it from bookmarklet. So here is updated code that insert jQuery script again – in use continuation to block execution of the script until jQuery is loaded). To use bookmarklet just copy code below and insert into url address bar when twitter tab is active

javascript:(function(continuation) {
    function attr(elem, key, value) {
        elem.setAttribute(document.createAttribute(key, value));
    }
    var script = (function() {
        var head = document.getElementsByTagName('head')[0];
        return function(src) {
            var script = document.createElement('script');
            script.setAttribute('src', src);
            script.setAttribute('type', 'text/javascript');
            script.setAttribute('async', 'false');
            head.appendChild(script);
            return script;
        };
    })();
    script('https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js');
    var interval = 100;
    setTimeout(function() {
        if (jQuery) {
            continuation(jQuery.noConflict());
        } else {
            setTimeout(arguments.callee, interval);
        }
    }, interval);
})(function($) {
    function translator(fun, lang) {
        var socket = new WebSocket("ws://localhost:8080/translate");
        socket.onopen = function() {
            console.log("Socket has been opened!");
        };
        socket.onclose = function() {
            console.log("closed");
        };
        return fun(function(text, respond) {
            socket.onmessage = function(msg) {
                respond(msg.data);
            };
            var data = JSON.stringify({to_lang: lang, text: text});
            socket.send(data);
        });
    }
    var translate_tweet = translator(function(translate) {
        return function(content) {
            var tweet_container = content.find('.js-tweet-text');
            var tweet = tweet_container.html();
            content.data('original', tweet);
            var links = [];
            var i = 0;
            tweet = tweet.replace(/<a[^>]+>[^<]+<\/a>|<a[^>]+><s>(#|@)<\/s><b>[^<]+<\/b><\/a>/g, function(link) {
                links.push(link);
                return '{' + i++ + '}';
            });
            translate(tweet, function(result) {
                for (var i=links.length; i--;) {
                    result = result.replace('{' + i + '}', links[i]);
                }
                tweet_container.html(result);
                var actions = content.find('.tweet-actions');
                if (actions.find('.action-orig-container').length == 0) {
                    actions.append('<li class="action-orig-container"><a href="#">Original</a></li>');
                }
            });
        };
    }, prompt('Select Language: af - afrikaans, sk - albánskej, ar - عربي, be - Беларускі, bg - Български, zh - 荃湾, zh - 太阳, hr - Hrvatski, cs - Český, da - Danske, et - Eesti, tl - filipiński, fi - Suomi, fr - Français, gl - galijski, el - Ελληνικά, iw - עברית, hi - हिन्दी, es - Español, nl - Nederlands, id - indonezyjski, ga - Gaeilge, is - Íslenska, ja - 日本語, yi - ייִדיש, ca - Català, ko - 한국의, lt - Lietuvos, lv - Latvijas, mk - Македонски, ms - Melayu, mt - Malti, de - Deutsch, no - Norsk, fa - فارسی, pl - polski, ru - Русский, ro - Română, sr - Српски, sk - Slovenský, sl - Slovenski, sw - Swahili, sv - Svenska, th - ภาษาไทย, tr - Türk, uk - Український, cy - walijski, hu - Magyar, vi - Việt, it - Italiano'));
    $('.content').unbind('mouseover').live('mouseover', function() {
        var $this = $(this);
        if ($this.find('.action-trans-container').length == 0) {
            $this.find('.action-fav-container').
                after('<li class="action-trans-container"><a href="#">Translate</a></li>');
        }
    });
    $('.action-orig-container').unbind('onclick').live('click', function() {
        var content = $(this).parents('.content');
        content.find('.js-tweet-text').html(content.data('original'));
        content.find('.action-orig-container').remove();
        return false;
    });
    $('.action-trans-container').unbind('onclick').live('click', function() {
        translate_tweet($(this).parents('.content'));
        return false;
    });
});

CODE LICENSE: you can use the code for whatever purpose you like it’s realeas on Sharing Agreement.

, ,

2 Comments

ERC notifications on channels where there was activity after some inactivity

I need to monitor few IRC channels and I use Emacs so I write simple elisp function that I append to erc-insert-pre-hook and it notify me when there is some activity on those channels. I made this mainly because I what to know if someone visit #openclipart channel (because people where visiting ask question and leave after few minutes, there is no much activity on this channel)

(setq inactivity-buffer-alist '(("#openclipart" (inactivity . 900))
                                ("#hackerrank" (inactivity . 900))
                                ("#aiki" (inactivity . 900))))

(defun channel-activity (string &rest ignore)
  "notification when there is activity on a erc channel after inactivity"
  (let* ((buffer (buffer-name))
         (buffer-alist-pair (assoc buffer inactivity-buffer-alist))
         (buffer-alist (cdr buffer-alist-pair))
         (current-time (current-time)))
    (if (not (null buffer-alist))
        (let ((last-time-pair (assoc 'last-time buffer-alist))
              (inactivity (cdr (assoc 'inactivity buffer-alist))))
          (if (not (and (string-match "^\\*\\*\\*" string)
                        (string-match "[freenode-info]" string)))
              (progn
                (if (or (null last-time-pair)
                        (> (float-time (time-subtract current-time
                                                      (cdr last-time-pair)))
                           inactivity))
                    (async-exec-command "mpg123 -q /home/kuba/Pobrane/beep-6.mp3"))
                (if (null last-time-pair)
                    (setf (cdr buffer-alist-pair)
                          (append buffer-alist
                                  (list (cons 'last-time current-time))))
                  (setf (cdr last-time-pair) current-time))))))))

(add-hook 'erc-insert-pre-hook 'channel-activity)

You can add your channels to inactivity-buffer-alist along with time of inactivity (in miliseconds)

The function I use for notification is play sound using (async-exec-command "mpg123 -q /home/kuba/Pobrane/beep-6.mp3") – normal shell command was stoping execution of Emacs for few seconds

The code for this function is as follow

(defun async-exec-command (command &rest success)
  (interactive)
  (let* ((buffer-name (generate-new-buffer-name "**shell**"))
         (buffer (get-buffer-create buffer-name))
         (process (apply #'start-process
                         (append (list buffer-name buffer)
                                 (split-string command " ")))))
    (lexical-let ((buffer buffer) (success (car success)) (command command))
      (set-process-sentinel process
                            (if success (lambda (process str)
                                          (if (string= str "finished\n")
                                              (save-excursion
                                                (set-buffer buffer)
                                                (let ((content (buffer-string)))
                                                  (kill-buffer buffer)
                                                  (funcall success content)))))
                              (lambda (proces str)
                                (kill-buffer buffer)))))
    (concat "execute: " command)))

, , ,

Leave a comment

How to have access to a shell without ssh or telnet using jQuery terminal

If you have, like I do, a virtual server on hosting service, that support cgi but don’t have access to a shell and php have passthru disabled you still can have access to a shell, even if you can’t install anything on that machine – like anyterm.

There is this project, which I did, called jQuery terminal, that emulate a Unix terminal. All you need is interpreter that will execute shell commands.

Lots of hosting services disable php passthru but leave cgi script, so if you have them you can create simple shell like this one:

UPDATE: If you don’t want to code it yourself (or look at working solution), you can check my project LEASH – Browser Shell.

#!/bin/bash
echo -en "Content-Type: text/plain\r\n\r\n"
query=$(/usr/bin/python -c "import urllib; print urllib.unquote_plus('$QUERY_STRING')")
eval $query 2>&1

I use python subprocess to decode QUERY_STRING. Probably it can be done using some Unix tools.

On my server I use Python CGI script like this one:.

#!/usr/bin/python
import cgi
import subprocess as sub

try:
    from json import dumps as json_serialize
except ImportError:
    def json_serialize(obj):
        result = ''
        t = type(obj)
        if t == types.StringType:
            result += '"%s"' % obj.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n')
        elif t == types.NoneType:
            result += 'null'
        elif t == types.IntType or t == types.FloatType:
            result += str(obj)
        elif t == types.LongType:
            result += str(int(obj))
        elif t == types.TupleType:
            result += '[' + ','.join(map(json_serialize, list(obj))) + ']'
        elif t == types.ListType:
            result += '[' + ','.join(map(json_serialize, obj)) + ']'
        elif t == types.DictType:
            array = ['"%s":%s' % (k,json_serialize(v)) for k,v in obj.iteritems()]
            result += '{' + ','.join(array) + '}'
        else:
            result += '"unknown type - ' + type(obj).__name__ + '"'
        return result

class Singleton(object):
    __single = None
    def __init__(self):
        Singleton.__single = self

    def __new__(cls):
        if Singleton.__single:
            return Singleton.__single
        else:
            return super(Singleton, cls).__new__(cls)


class Form(Singleton):
    def __init__(self):
        super(Form)
        self.form = cgi.FieldStorage()

    def __getitem__(self, name):
        return self.form.getvalue(name)

def execv(command, path):
    command = 'cd %s && %s && pwd 1>&2' % (path, command)
    proc = sub.Popen(['/bin/bash', '-c', command],
                     stdout=sub.PIPE, stderr=sub.PIPE)
    stderr = proc.stderr.read()[:-1]
    stdout = proc.stdout.read()[:-1]
    if not os.path.exists(stderr):
        raise Exception(stdout, stderr)
    return {
        "cwd": stderr,
        "stdout": re.sub('.\x08', '', stdout)
    }

if __name__ == '__main__':
    print "Content-Type: text/plain"
    print
    form = Form()
    if token() == form['token']:
        response = {}
        try:
            response['result'] = execv(form['command'], form['path'])
        except Exception, e:
            response['error'] = e.args[1]
            response['result'] = {'stdout': e.args[0], 'cwd': form['path']}
        else:
            response['error'] = None
    else:
        response = {'error': 'You are not authorized', 'result': None}
    stdout.write(json_serialize(response))

You have enabled passthru in php you can write your shell in php.

Now when you have access to a shell you need to setup interface using jQuery terminal.

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Shell</title>
    <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
    <script src="terminal/js/jquery.mousewheel-min.js"></script>
    <script src="terminal/js/jquery.terminal.js"></script>
    <link href="terminal/css/jquery.terminal.css" rel="stylesheet"/>
    <style>
    .terminal a.ui-slider-handle:focus { outline: none; }
    body { margin: 0; padding: 0; }
    html { background-color: #000; }
    .clear { clear: both; }
    /* This works only in Safari and Google Chrome */
    @media screen and (-webkit-min-device-pixel-ratio:0) {
        .terminal, .terminal .terminal-output, .terminal .terminal-output div,
        .terminal .terminal-output div div, .cmd, .terminal .cmd span, .terminal .cmd div {
            font-weight: bold;
        }
    }
    </style>
<script>
$(function() {
    var pwd, last_dir, home_dir;
    var terminal = $('#shell').terminal(function(command, term) {
        if (command.replace(/^ *(.*) *$/, '$1') == '-') {
            pwd = last_dir;
            return;
        }
        term.pause();
        $.post("/cgi-bin/cmd.py", {
            token: term.token(),
            command: command.replace(/(\\)?~/, function($0,$1) {
                return $1 ? $0 : home_dir;
            }),
            path: pwd
        }, function(response) {
            term.echo(response.result.stdout);
            pwd = response.result.cwd;
            term.resume();
        });
    }, {
        login: function(user, password, authenticate) {
            $.post("/cgi-bin/cmd.py", {
                user: user,
                password: password
            }, function(token) {
                if (token) {
                    home_dir = pwd = last_dir = '/home/ + user;
                }
                authenticate(token);
            });
        },
        prompt: function(callback) {
            var username = terminal.login_name();
            var re = new RegExp("^" + '/home/' + username);
            var username = '[[;#44D544;]' + username + ']';
            var path = '[[;#5555FF;]' + pwd.replace(re, '~') + ']';
            callback(username + '[[;#989898;]:]' + path + '[[;#989898;]$] ');
        },
        name: 'shell'
    }).css({
        overflow: 'auto'
    });

    $(window).resize(function() {
       terminal.css('height', $(window).height()-20);
    }).resize();

});
</script>
</head>
<body>
  <div id="shell"></div>
</body>
</html>

, ,

1 Comment

My Poster for Libre Graphics Meeting

Libre Graphics Meeting – Free Graphic Design Conference for Free Libre and Open Source Software

Libre Graphics Meeting - Free Graphic Design Conference for Open Source Software

My Poster to Libre Graphics Meeting Conference – conference about open source software use to create graphics of any kind. you can also download pdf version

Help make this event happen – donate

Visit official site for the conference

Follow LGM on twitter

Do you want to be on a poster? – Become a sponsor

See promoting Video from Last Year

, ,

1 Comment

Switching between buffers with the same major mode in Emacs

Below are functions that can be used to switch to next or previus buffer in the same major mode

(defun buffer-same-mode (change-buffer-fun)
  (let ((current-mode major-mode)
        (next-mode nil))
    (while (not (eq next-mode current-mode))
      (funcall change-buffer-fun)
      (setq next-mode major-mode))))

(defun previous-buffer-same-mode ()
  (interactive)
  (buffer-same-mode #'previous-buffer))

(defun next-buffer-same-mode ()
  (interactive)
  (buffer-same-mode #'next-buffer))

In my init file I have bind those functions to CTRL+TAB and CTRL+ALT+TAB which was not set by default.

(global-set-key [C-M-tab] 'previous-buffer-same-mode)
(global-set-key [C-tab] 'next-buffer-same-mode)

,

Leave a comment

Faster buffer bookmarking in Emacs

I wanted to speed up jumping in the same buffer so I have written this bookmarking utility (Similar to built-in registers) and put it in my .emacs file.

(defvar bookmark-markers '())

(defun bookmark (bookmark)
   "Store current position for this buffer in 
    bookmar-markers a-list"
   (interactive "nBookmark: ")
   (let* ((buffer (current-buffer))
          (bookmarks
           (let ((pair (assoc buffer bookmark-markers)))
             (if (eq pair nil)
                 (let ((new-pair (cons buffer '())))
                   (progn
                     (setq bookmark-markers
                           (append bookmark-markers
                                   (list new-pair)))
                     new-pair))
              pair))))
     (let ((pair (assoc bookmark bookmarks)))
       (if (eq pair nil)
           (setf (cdr bookmarks)
                 (append (cdr bookmarks)
                         (list (cons bookmark (point)))))
         (setf (cdr pair) (point))))))

(defun jump-to-bookmark (bookmark)
  "Jump to previously stored bookmark position"
  (interactive "nJump To: ")
  (let ((pair-bookmars (assoc (current-buffer) bookmark-markers)))
    (if (not (eq pair-bookmars nil))
        (let ((pair-point (assoc bookmark (cdr pair-bookmars))))
          (if (not (eq pair-point nil))
              (goto-char (cdr pair-point)))))))

(defun range (n &optional list)
  "function return list of numbers from 1 to n"
  (if (eq n 0)
      list
    (let ((n (- n 1)))
      (range n (cons n list)))))

(dolist (i (range 9))
    (global-set-key (read-kbd-macro (concat "C-c "
                                            (number-to-string i)))
                    ;; emacs lisp have no closures
                    (lexical-let ((i i)) 
                      (lambda ()
                        (interactive)
                        (jump-to-bookmark i)))))

(global-set-key (kbd "C-c 0") 'bookmark)

Above code define 2 functions bookmark bind to C-c 0 and function jump-to-bookmark this function create a bookmark, for currect position in a buffer, and assing it to the number (passed as argument or from minubuffer, if run interactively). You have 9 keyboard binding for keys from C-c 1 to C-c 9.

You can use it go to specific location and type C-c 0 1 RET go to another location and type C-c 0 2 RET and now you can jump to locations with C-c 1 or C-c 2.

Every buffer will have they own bookmarks.

,

Leave a comment

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: