~ubuntu-branches/ubuntu/natty/pida/natty

« back to all changes in this revision

Viewing changes to pida/editors/emacs/emacs.py

  • Committer: Bazaar Package Importer
  • Author(s): Jan Luebbe
  • Date: 2007-09-05 17:54:09 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20070905175409-ty9f6qpuctyjv1sd
Tags: 0.5.1-2
* Depend on librsvg2-common, which is not pulled in by the other depends
  (closes: #394860)
* gvim is no alternative for python-gnome2 and python-gnome2-extras
  (closes: #436431)
* Pida now uses ~/.pida2, so it can no longer be confused by old
  configurations (closes: #421378)
* Culebra is no longer supported by upstream (closes: #349009)
* Update manpage (closes: #440375)
* Update watchfile (pida is now called PIDA)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*- 
 
2
 
 
3
# Copyright (c) 2007 The PIDA Project
 
4
 
 
5
#Permission is hereby granted, free of charge, to any person obtaining a copy
 
6
#of this software and associated documentation files (the "Software"), to deal
 
7
#in the Software without restriction, including without limitation the rights
 
8
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
9
#copies of the Software, and to permit persons to whom the Software is
 
10
#furnished to do so, subject to the following conditions:
 
11
 
 
12
#The above copyright notice and this permission notice shall be included in
 
13
#all copies or substantial portions of the Software.
 
14
 
 
15
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
16
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
17
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
18
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
19
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
20
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
21
#SOFTWARE.
 
22
 
 
23
"""The Emacs editor core classes for Pida.
 
24
 
 
25
This module and other Pida Emacs related classes are based on preliminary works
 
26
by Ali Afshar (see Emacs module in Pida 0.2.2).
 
27
 
 
28
The Emacs editor for Pida is also, heavily based on the Vim editor.
 
29
"""
 
30
 
 
31
 
 
32
import logging
 
33
import os
 
34
import gobject
 
35
import gtk
 
36
 
 
37
# PIDA Imports
 
38
from pida.ui.views import PidaView
 
39
 
 
40
from pida.core.log import build_logger
 
41
from pida.core.editors import EditorService, _
 
42
 
 
43
# Emacs specific
 
44
from pida.utils.emacs.emacsembed import EmacsEmbedWidget
 
45
from pida.utils.emacs.emacscom import EmacsClient, EmacsServer, EMACS_SCRIPT
 
46
 
 
47
 
 
48
class EmacsView(PidaView):
 
49
 
 
50
    def create_ui(self):
 
51
        self._emacs = EmacsEmbedWidget('emacs', self.svc.script_path)
 
52
        self.add_main_widget(self._emacs)
 
53
 
 
54
    def run(self):
 
55
        self._emacs.run()
 
56
 
 
57
    def grab_input_focus(self):
 
58
        self._emacs.grab_input_focus()
 
59
 
 
60
 
 
61
class EmacsCallback(object):
 
62
    """Emacs editor callback behaviours.
 
63
 
 
64
    Communication with Emacs process is handled by EmacsClient in the pIDA->Emacs
 
65
    way, and EmacsServer the other way. On occurence of a message, EmacsServer
 
66
    extracts a request name and arguments, and then tries to invoke the matching
 
67
    method on the EmacsCallback object.
 
68
    
 
69
    Callbacks' names are built with the Emacs message names, prefixed with 'cb_'.
 
70
    Each callback accepts exactly one argument.
 
71
    """
 
72
 
 
73
    def __init__(self, svc):
 
74
        """Constructor."""
 
75
        self._log = logging.getLogger('emacs')
 
76
        self._svc = svc
 
77
        self._server = EmacsServer(self)
 
78
 
 
79
    def connect(self):
 
80
        """Establish the link with Emacs."""
 
81
        return self._server.connect()
 
82
 
 
83
    def cb_pida_pong(self, foo):
 
84
        """Emacs response to a ping.
 
85
 
 
86
        This message is used to test connection at startup.
 
87
        """
 
88
        self._log.debug('emacs ready')
 
89
        self._svc.emit_editor_started()
 
90
        return True
 
91
 
 
92
    def cb_window_configuration_change_hook(self, filename):
 
93
        """Buffer changed event.
 
94
 
 
95
        Actually, this hook is called whenever the window containing the
 
96
        buffer changes. So notification can occur only when window is resized
 
97
        or split for example.
 
98
        """
 
99
        self._svc.top_buffer = filename
 
100
        current = self._svc.current_document
 
101
        if filename and (not current or current.filename != filename):
 
102
            self._log.debug('emacs buffer changed "%s"' % filename)
 
103
            if os.path.isdir(filename):
 
104
                self._svc.boss.cmd('filemanager', 'browse', new_path=filename)
 
105
                self._svc.boss.cmd('filemanager', 'present_view')
 
106
            else:
 
107
                self._svc.boss.cmd('buffer', 'open_file', file_name=filename)
 
108
        return True
 
109
    
 
110
    def cb_kill_buffer_hook(self, filename):
 
111
        """Buffer closed event."""
 
112
        if filename:
 
113
            self._log.debug('emacs buffer killed "%s"' % filename)
 
114
            self._svc.remove_file(filename)
 
115
            self._svc.boss.get_service('buffer').cmd('close_file', file_name=filename)
 
116
        return True
 
117
 
 
118
    def cb_find_file_hooks(self, filename):
 
119
        """File opened event."""
 
120
        # Nothing to do here. The window configuration change hook will
 
121
        # provide notification for the new buffer.
 
122
        if filename:
 
123
            self._log.debug('emacs buffer opened "%s"' % filename)
 
124
        return True
 
125
    
 
126
    def cb_after_save_hook(self, filename):
 
127
        """Buffer saved event."""
 
128
        self._log.debug('emacs buffer saved "%s"' % filename)
 
129
        self._svc.boss.cmd('buffer', 'current_file_saved')
 
130
        return True
 
131
    
 
132
    def cb_kill_emacs_hook(self, foo):
 
133
        """Emacs killed event."""
 
134
        self._log.debug('emacs killed')
 
135
        self._svc.inactivate_client()
 
136
        self._svc.boss.stop(force=True)
 
137
        return False
 
138
 
 
139
# Service class
 
140
class Emacs(EditorService):
 
141
    """The Emacs service.
 
142
 
 
143
    This service is the Emacs editor driver. Emacs instance creation is decided
 
144
    there and orders for Emacs are sent to it which forwards them to the
 
145
    EmacsClient instance. 
 
146
    """ 
 
147
 
 
148
    def _create_initscript(self):
 
149
        self.script_path = os.path.join(
 
150
            self.boss.get_pida_home(), 'pida_emacs_init.el')
 
151
        f = open(self.script_path, 'w')
 
152
        f.write(EMACS_SCRIPT)
 
153
        f.close()
 
154
 
 
155
    def emit_editor_started(self):
 
156
        self.boss.get_service('editor').emit('started')
 
157
 
 
158
    def pre_start(self):
 
159
        """Start the editor"""
 
160
        self._log = build_logger('emacs')
 
161
        self._create_initscript()
 
162
        self._documents = {}
 
163
 
 
164
        # The current document. Its value is set by Pida and used to drop
 
165
        # useless messages to emacs.
 
166
        self._current = None
 
167
 
 
168
        # The current buffer displayed. Its value is set by the EmacsCallback
 
169
        # instance and is used as well to prevent sending useless messages.
 
170
        self._top_buffer = ''
 
171
 
 
172
        self._current_line = 1
 
173
        self._cb = EmacsCallback(self)
 
174
        self._client = EmacsClient()
 
175
        self._view = EmacsView(self)
 
176
 
 
177
        # Add the view to the top level window. Only after that, it will be
 
178
        # possible to add a socket in the view.
 
179
        self.boss.cmd('window', 'add_view', paned='Editor', view=self._view)
 
180
 
 
181
        # Now create the socket and embed the Emacs window.
 
182
        self._view.run()
 
183
        if self._cb.connect():
 
184
            gobject.timeout_add(250, self._client.ping)
 
185
 
 
186
    def stop(self):
 
187
        self._client.quit()
 
188
 
 
189
    def _get_current_document(self):
 
190
        return self._current
 
191
 
 
192
    def _set_current_document(self, document):
 
193
        self._current = document
 
194
 
 
195
    current_document = property(fget=_get_current_document,
 
196
                                fset=_set_current_document,
 
197
                                fdel=None,
 
198
                                doc="The document currently edited")
 
199
 
 
200
    def _get_top_buffer(self):
 
201
        return self._top_buffer
 
202
 
 
203
    def _set_top_buffer(self, filename):
 
204
        self._top_buffer = filename
 
205
 
 
206
    top_buffer = property(fget=_get_top_buffer,
 
207
                          fset=_set_top_buffer,
 
208
                          fdel=None,
 
209
                          doc="The last buffer reported as being viewed by emacs")
 
210
 
 
211
    def inactivate_client(self):
 
212
        self._client.inactivate()
 
213
 
 
214
    def open(self, document):
 
215
        """Open a document"""
 
216
        if document is not self._current:
 
217
            if self.top_buffer != document.filename:
 
218
                if document.unique_id in self._documents:
 
219
                    self._client.change_buffer(document.filename)
 
220
                else:
 
221
                    self._client.open_file(document.filename)
 
222
                    self._documents[document.unique_id] = document
 
223
            self.current_document = document
 
224
 
 
225
    def open_many(documents):
 
226
        """Open a few documents"""
 
227
        pass
 
228
    
 
229
    def close(self, document):
 
230
        if document.unique_id in self._documents:
 
231
            self._remove_document(document)
 
232
            self._client.close_buffer(document.filename)
 
233
 
 
234
    def remove_file(self, filename):
 
235
        document = self._get_document_for_filename(filename)
 
236
        if document is not None:
 
237
            self._remove_document(document)
 
238
 
 
239
    def _remove_document(self, document):
 
240
        del self._documents[document.unique_id]
 
241
 
 
242
    def _get_document_for_filename(self, filename):
 
243
        for uid, doc in self._documents.iteritems():
 
244
            if doc.filename == filename:
 
245
                return doc
 
246
            
 
247
    def close_all(self):
 
248
        """Close all the documents"""
 
249
 
 
250
    def save(self):
 
251
        """Save the current document"""
 
252
        self._client.save_buffer()
 
253
 
 
254
    def save_as(self, filename):
 
255
        """Save the current document as another filename"""
 
256
        pass # TODO
 
257
 
 
258
    def revert(self):
 
259
        """Revert to the loaded version of the file"""
 
260
        self._client.revert_buffer()
 
261
 
 
262
    def goto_line(self, line):
 
263
        """Goto a line"""
 
264
        self._client.goto_line(line + 1)
 
265
        self.grab_focus()
 
266
 
 
267
    def cut(self):
 
268
        """Cut to the clipboard"""
 
269
        self._client.cut()
 
270
 
 
271
    def copy(self):
 
272
        """Copy to the clipboard"""
 
273
        self._client.copy()
 
274
 
 
275
    def paste(self):
 
276
        """Paste from the clipboard"""
 
277
        self._client.paste()
 
278
 
 
279
    def undo(self):
 
280
        self._client.undo()
 
281
 
 
282
    def redo(self):
 
283
        self._client.redo()
 
284
 
 
285
    def grab_focus(self):
 
286
        """Grab the focus"""
 
287
        self._view.grab_input_focus()
 
288
 
 
289
    def define_sign_type(self, name, icon, linehl, text, texthl):
 
290
        # TODO
 
291
        pass
 
292
    
 
293
    def undefine_sign_type(self, name):
 
294
        # TODO
 
295
        pass
 
296
    
 
297
    #def _add_sign(self, type, filename, line):
 
298
        
 
299
    #def _del_sign(self, type, filename, line):
 
300
 
 
301
    def show_sign(self, type, filename, line):
 
302
        # TODO
 
303
        pass
 
304
    
 
305
    def hide_sign(self, type, filename, line):
 
306
        # TODO
 
307
        pass
 
308
    
 
309
    def set_current_line(self, line_number):
 
310
        self._current_line = line_number
 
311
 
 
312
    def get_current_line(self):
 
313
        return self._current_line
 
314
 
 
315
    #def call_with_current_word(self, callback):
 
316
    #   return self._com.get_cword(self.server, callback)
 
317
 
 
318
    #def call_with_selection(self, callback):
 
319
    #   return self._com.get_selection(self.server, callback)
 
320
 
 
321
    def set_path(self, path):
 
322
        return self._client.set_directory(path)        
 
323
 
 
324
 
 
325
# Required Service attribute for service loading
 
326
Service = Emacs
 
327
 
 
328
 
 
329
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: