home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / gedit-2 / plugins / snippets / SnippetPlaceholders.py < prev    next >
Encoding:
Python Source  |  2006-08-27  |  12.3 KB  |  507 lines

  1. #    Gedit snippets plugin
  2. #    Copyright (C) 2005-2006  Jesse van den Kieboom <jesse@icecrew.nl>
  3. #
  4. #    This program is free software; you can redistribute it and/or modify
  5. #    it under the terms of the GNU General Public License as published by
  6. #    the Free Software Foundation; either version 2 of the License, or
  7. #    (at your option) any later version.
  8. #
  9. #    This program is distributed in the hope that it will be useful,
  10. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. #    GNU General Public License for more details.
  13. #
  14. #    You should have received a copy of the GNU General Public License
  15. #    along with this program; if not, write to the Free Software
  16. #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17.  
  18. import traceback
  19. import re
  20. from functions import *
  21. import os
  22. import sys
  23. import signal
  24. import gobject
  25. import select
  26. import locale
  27. import subprocess
  28.  
  29. # These are places in a view where the cursor can go and do things
  30. class SnippetPlaceholder:
  31.     def __init__(self, view, tabstop, default, begin):
  32.         self.ok = True
  33.         self.buf = view.get_buffer()
  34.         self.view = view
  35.         self.mirrors = []
  36.         self.leave_mirrors = []
  37.         self.tabstop = tabstop
  38.         self.default = self.expand_environment(default)
  39.         self.prev_contents = self.default
  40.         
  41.         if begin:
  42.             self.begin = self.buf.create_mark(None, begin, True)
  43.         else:
  44.             self.begin = None
  45.         
  46.         self.end = None
  47.     
  48.     def literal(self, s):
  49.         return repr(s)
  50.  
  51.     def re_environment(self, m):
  52.         if m.group(1) or not m.group(2) in os.environ:
  53.             return '"$' + m.group(2) + '"'
  54.         else:
  55.             return os.environ[m.group(2)]
  56.  
  57.     def expand_environment(self, text):
  58.         if not text:
  59.             return text
  60.  
  61.         return re.sub('(\\\\)?\\$([A-Z_]+)', self.re_environment, text)
  62.     
  63.     def get_iter(self, mark):
  64.         if mark and not mark.get_deleted():
  65.             return self.buf.get_iter_at_mark(mark)
  66.         else:
  67.             return None
  68.  
  69.     def begin_iter(self):
  70.         return self.get_iter(self.begin)
  71.     
  72.     def end_iter(self):
  73.         return self.get_iter(self.end)
  74.     
  75.     def run_last(self, placeholders):
  76.         begin = self.begin_iter()
  77.         self.end = self.buf.create_mark(None, begin, False)
  78.         
  79.         if self.default:
  80.             insert_with_indent(self.view, begin, self.default, False)
  81.     
  82.     def remove(self, force = False):
  83.         if self.begin and not self.begin.get_deleted():
  84.             self.buf.delete_mark(self.begin)
  85.         
  86.         if self.end and not self.end.get_deleted():
  87.             self.buf.delete_mark(self.end)
  88.         
  89.     # Do something on beginning this placeholder
  90.     def enter(self):
  91.         self.buf.move_mark(self.buf.get_insert(), self.begin_iter())
  92.  
  93.         if self.end:
  94.             self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter())
  95.         else:
  96.             self.buf.move_mark(self.buf.get_selection_bound(), self.begin_iter())
  97.     
  98.     def get_text(self):
  99.         if self.begin and self.end:
  100.             return self.buf.get_text(self.begin_iter(), self.end_iter())
  101.         else:
  102.             return ''
  103.     
  104.     def add_mirror(self, mirror):
  105.         if isinstance(mirror, SnippetPlaceholderExpand):
  106.             self.leave_mirrors.append(mirror)
  107.         else:
  108.             self.mirrors.append(mirror)
  109.  
  110.     def set_text(self, text):
  111.         if self.begin.get_deleted() or self.end.get_deleted():
  112.             return
  113.  
  114.         # Set from self.begin to self.end to text!
  115.         self.buf.begin_user_action()
  116.         # Remove everything between self.begin and self.end
  117.         begin = self.begin_iter()
  118.         self.buf.delete(begin, self.end_iter())
  119.         # Insert the text from the mirror
  120.  
  121.         insert_with_indent(self.view, begin, text, True)
  122.         self.buf.end_user_action()
  123.         
  124.         self.update_contents()
  125.  
  126.     def update_contents(self):
  127.         prev = self.prev_contents
  128.         self.prev_contents = self.get_text()
  129.         
  130.         if prev != self.get_text():
  131.             for mirror in self.mirrors:
  132.                 if not mirror.update(self):
  133.                     return
  134.             
  135.     # Do something on ending this placeholder
  136.     def leave(self):
  137.         # Notify mirrors
  138.         for mirror in self.leave_mirrors:
  139.             if not mirror.update(self):
  140.                 return
  141.  
  142. # This is an placeholder which inserts a mirror of another SnippetPlaceholder    
  143. class SnippetPlaceholderMirror(SnippetPlaceholder):
  144.     def __init__(self, view, tabstop, begin):
  145.         SnippetPlaceholder.__init__(self, view, -1, None, begin)
  146.         self.mirror_stop = tabstop
  147.  
  148.     def update(self, mirror):
  149.         self.set_text(mirror.get_text())
  150.         return True
  151.     
  152.     def run_last(self, placeholders):
  153.         SnippetPlaceholder.run_last(self, placeholders)
  154.         
  155.         if placeholders.has_key(self.mirror_stop):
  156.             mirror = placeholders[self.mirror_stop]
  157.             
  158.             mirror.add_mirror(self)
  159.             
  160.             if mirror.default:
  161.                 self.set_text(mirror.default)
  162.         else:
  163.             self.ok = False
  164.  
  165. # This placeholder indicates the end of a snippet
  166. class SnippetPlaceholderEnd(SnippetPlaceholder):
  167.     def __init__(self, view, begin, default):
  168.         SnippetPlaceholder.__init__(self, view, 0, default, begin)
  169.     
  170.     def run_last(self, placeholders):
  171.         SnippetPlaceholder.run_last(self, placeholders)
  172.         
  173.         # Remove the begin mark and set the begin mark
  174.         # to the end mark, this is needed so the end placeholder won't contain
  175.         # any text
  176.         
  177.         if not self.default:
  178.             self.buf.delete_mark(self.begin)
  179.             self.begin = self.buf.create_mark(None, self.end_iter(), False)
  180.  
  181.     def enter(self):
  182.         self.buf.move_mark(self.buf.get_insert(), self.begin_iter())
  183.         self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter())
  184.         
  185.     def leave(self):
  186.         self.enter()
  187.  
  188. # This placeholder is used to expand a command with embedded mirrors    
  189. class SnippetPlaceholderExpand(SnippetPlaceholder):
  190.     def __init__(self, view, tabstop, begin, s):
  191.         SnippetPlaceholder.__init__(self, view, tabstop, None, begin)
  192.  
  193.         self.mirror_text = {0: ''}
  194.         self.timeout_id = None
  195.         self.cmd = s
  196.  
  197.     def get_mirrors(self, placeholders):
  198.         s = self.cmd
  199.         mirrors = []        
  200.  
  201.         while (True):
  202.             m = re.search('\\${([0-9]+)}', s) or re.search('\\$([0-9]+)', s)
  203.             
  204.             if not m:
  205.                 break
  206.  
  207.             tabstop = int(m.group(1))
  208.             
  209.             if placeholders.has_key(tabstop):
  210.                 if not tabstop in mirrors:
  211.                     mirrors.append(tabstop)
  212.  
  213.                 s = s[m.end():]
  214.             else:
  215.                 self.ok = False
  216.                 return None
  217.         
  218.         return mirrors
  219.         
  220.     # Check if all substitution placeholders are accounted for
  221.     def run_last(self, placeholders):
  222.         SnippetPlaceholder.run_last(self, placeholders)
  223.  
  224.         self.ok = True
  225.         mirrors = self.get_mirrors(placeholders)
  226.     
  227.         if mirrors:
  228.             allDefault = True
  229.                 
  230.             for mirror in mirrors:
  231.                 p = placeholders[mirror]
  232.                 p.add_mirror(self)
  233.                 self.mirror_text[p.tabstop] = p.default
  234.                 
  235.                 if not p.default:
  236.                     allDefault = False
  237.             
  238.             if allDefault:
  239.                 self.expand(self.substitute())
  240.         else:
  241.             self.update(None)
  242.             self.ok = False
  243.         
  244.     def re_placeholder(self, m):
  245.         if m.group(1):
  246.             return '"$' + m.group(2) + '"'
  247.         else:
  248.             if m.group(3):
  249.                 index = int(m.group(3))
  250.             else:
  251.                 index = int(m.group(4))
  252.             
  253.             return SnippetPlaceholder.literal(self, self.mirror_text[index])
  254.  
  255.     def remove_timeout(self):
  256.         if self.timeout_id != None:
  257.             gobject.source_remove(self.timeout_id)
  258.             self.timeout_id = None
  259.         
  260.     def install_timeout(self):
  261.         self.remove_timeout()
  262.         self.timeout_id = gobject.timeout_add(1000, self.timeout_cb)
  263.  
  264.     def timeout_cb(self):
  265.         self.timeout_id = None
  266.         
  267.         return False
  268.         
  269.     def substitute(self):
  270.         # substitute all mirrors, but also environmental variables
  271.         text = re.sub('(\\\\)?\\$({([0-9]+)}|([0-9]+))', self.re_placeholder, 
  272.                 self.cmd)
  273.         
  274.         return SnippetPlaceholder.expand_environment(self, text)
  275.     
  276.     def update(self, mirror):
  277.         text = None
  278.         
  279.         if mirror:
  280.             self.mirror_text[mirror.tabstop] = mirror.get_text()
  281.  
  282.             # Check if all substitutions have been made
  283.             for tabstop in self.mirror_text:
  284.                 if tabstop == 0:
  285.                     continue
  286.  
  287.                 if not self.mirror_text[tabstop]:
  288.                     return False
  289.             
  290.         else:
  291.             self.ok = False
  292.  
  293.         text = self.substitute()
  294.         
  295.         if text:
  296.             return self.expand(text)
  297.         
  298.         return True
  299.  
  300.     def expand(self, text):
  301.         return True
  302.  
  303. # The shell placeholder executes commands in a subshell
  304. class SnippetPlaceholderShell(SnippetPlaceholderExpand):
  305.     def __init__(self, view, tabstop, begin, s):
  306.         SnippetPlaceholderExpand.__init__(self, view, tabstop, begin, s)
  307.  
  308.         self.shell = None
  309.         self.remove_me = False
  310.  
  311.     def close_shell(self):
  312.         self.shell.stdout.close()
  313.         self.shell = None    
  314.     
  315.     def timeout_cb(self):
  316.         SnippetPlaceholderExpand.timeout_cb(self)
  317.         self.remove_timeout()
  318.         
  319.         if not self.shell:
  320.             return False
  321.  
  322.         gobject.source_remove(self.watch_id)
  323.         self.close_shell()
  324.  
  325.         if self.remove_me:
  326.             SnippetPlaceholderExpand.remove(self)
  327.  
  328.         message_dialog(None, gtk.MESSAGE_ERROR, 'Execution of the shell ' \
  329.                 'command (%s) exceeds the maximum time, ' \
  330.                 'execution aborted.' % self.command)
  331.         
  332.         return False
  333.     
  334.     def process_close(self):
  335.             self.close_shell()
  336.             self.remove_timeout()
  337.  
  338.             self.set_text(str.join('', self.shell_output))
  339.             
  340.             if self.remove_me:
  341.                 SnippetPlaceholderExpand.remove(self, True)
  342.         
  343.     def process_cb(self, source, condition):
  344.         if condition & gobject.IO_IN:
  345.             line = source.readline()
  346.  
  347.             if len(line) > 0:
  348.                 try:
  349.                     line = unicode(line, 'utf-8')
  350.                 except:
  351.                     line = unicode(line, locale.getdefaultlocale()[1], 
  352.                             'replace')
  353.  
  354.             self.shell_output += line
  355.             self.install_timeout()
  356.  
  357.             return True
  358.  
  359.         self.process_close()
  360.         return False
  361.         
  362.     def expand(self, text):
  363.         self.remove_timeout()
  364.  
  365.         if self.shell:
  366.             gobject.source_remove(self.watch_id)
  367.             self.close_shell()
  368.  
  369.         popen_args = {
  370.             'cwd'  : None,
  371.             'shell': True,
  372.             'env'  : os.environ,
  373.             'stdout': subprocess.PIPE
  374.         }
  375.  
  376.         self.command = text
  377.         self.shell = subprocess.Popen(text, **popen_args)
  378.         self.shell_output = ''
  379.         self.watch_id = gobject.io_add_watch(self.shell.stdout, gobject.IO_IN | \
  380.                 gobject.IO_HUP, self.process_cb)
  381.         self.install_timeout()
  382.         
  383.         return True
  384.         
  385.     def remove(self, force = False):
  386.         if not force and self.shell:
  387.             # Still executing shell command
  388.             self.remove_me = True
  389.         else:
  390.             if force:
  391.                 self.remove_timeout()
  392.                 
  393.                 if self.shell:
  394.                     self.close_shell()
  395.  
  396.             SnippetPlaceholderExpand.remove(self, force)
  397.  
  398. class TimeoutError(Exception):
  399.     def __init__(self, value):
  400.         self.value = value
  401.     
  402.     def __str__(self):
  403.         return repr(self.value)
  404.  
  405. # The python placeholder evaluates commands in python
  406. class SnippetPlaceholderEval(SnippetPlaceholderExpand):
  407.     def __init__(self, view, refs, begin, s, namespace):
  408.         SnippetPlaceholderExpand.__init__(self, view, -1, begin, s)
  409.  
  410.         self.fdread = 0
  411.         self.remove_me = False
  412.         self.namespace = namespace
  413.         
  414.         self.refs = []
  415.         
  416.         if refs:
  417.             for ref in refs:
  418.                 self.refs.append(int(ref.strip()))
  419.     
  420.     def get_mirrors(self, placeholders):
  421.         mirrors = SnippetPlaceholderExpand.get_mirrors(self, placeholders)
  422.         
  423.         if not self.ok:
  424.             return None
  425.         
  426.         for ref in self.refs:
  427.             if placeholders.has_key(ref):
  428.                 if not ref in mirrors:
  429.                     mirrors.append(ref)
  430.             else:
  431.                 self.ok = False
  432.                 return None
  433.         
  434.         return mirrors
  435.     
  436.     def timeout_cb(self, signum = 0, frame = 0):
  437.         raise TimeoutError, "Operation timed out (>2 seconds)"
  438.     
  439.     def install_timeout(self):
  440.         if self.timeout_id != None:
  441.             self.remove_timeout()
  442.         
  443.         self.timeout_id = signal.signal(signal.SIGALRM, self.timeout_cb)
  444.         signal.alarm(2)
  445.         
  446.     def remove_timeout(self):
  447.         if self.timeout_id != None:
  448.             signal.alarm(0)
  449.             
  450.             signal.signal(signal.SIGALRM, self.timeout_id)
  451.  
  452.             self.timeout_id = None
  453.         
  454.     def expand(self, text):
  455.         self.remove_timeout()
  456.  
  457.         text = text.strip()
  458.         self.command = text
  459.  
  460.         if not self.command or self.command == '':
  461.             self.set_text('')
  462.             return
  463.  
  464.         text = "def process_snippet():\n\t" + "\n\t".join(text.split("\n"))
  465.         
  466.         if 'process_snippet' in self.namespace:
  467.             del self.namespace['process_snippet']
  468.  
  469.         try:
  470.             exec text in self.namespace
  471.         except:
  472.             traceback.print_exc()
  473.  
  474.         if 'process_snippet' in self.namespace:
  475.             try:
  476.                 # Install a sigalarm signal. This is a HACK to make sure 
  477.                 # gedit doesn't get freezed by someone creating a python
  478.                 # placeholder which for instance loops indefinately. Since
  479.                 # the code is executed synchronously it will hang gedit. With
  480.                 # the alarm signal we raise an exception and catch this
  481.                 # (see below). We show an error message and return False.
  482.                 # ___this is a HACK___ and should be fixed properly (I just 
  483.                 # don't know how)                
  484.                 self.install_timeout()
  485.                 result = self.namespace['process_snippet']()
  486.                 self.remove_timeout()
  487.             except TimeoutError:
  488.                 self.remove_timeout()
  489.  
  490.                 message_dialog(None, gtk.MESSAGE_ERROR, \
  491.                 _('Execution of the python command (%s) exceeds the maximum ' \
  492.                 'time, execution aborted.') % self.command)
  493.                 
  494.                 return False
  495.             except Exception, detail:
  496.                 self.remove_timeout()
  497.                 
  498.                 message_dialog(None, gtk.MESSAGE_ERROR, 
  499.                 _('Execution of the python command (%s) failed: %s') % 
  500.                 (self.command, detail))
  501.  
  502.                 return False
  503.  
  504.             self.set_text(result)
  505.         
  506.         return True
  507.