Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored and updated the code #14

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 133 additions & 118 deletions gedi.py
Original file line number Diff line number Diff line change
@@ -1,122 +1,137 @@
import os
import tempfile
import subprocess

from jedi.api import Script
from gi.repository import GObject, Gedit, Gtk, GtkSource

#FIXME: find real icon names
icon_names = {'import': '',
'module': '',
'class': '',
'function': '',
'statement': '',
'param': ''}

class Jedi:
def get_script(document):
doc_text = document.get_text(document.get_start_iter(), document.get_end_iter(), False)
iter_cursor = document.get_iter_at_mark(document.get_insert())
linenum = iter_cursor.get_line() + 1
charnum = iter_cursor.get_line_index()

return Script(doc_text, linenum, charnum, document.get_uri_for_display())

#!/usr/bin/env python3

import jedi, os.path, threading
from gi.repository import Gtk, GLib, Gedit, GObject, GdkPixbuf, GtkSource

# TODO: add settings menu
jedi.settings.fast_parser = False # thread-safety
jedi.settings.case_insensitive_completion = False # case-sensitive
jedi.settings.add_bracket_after_function = True # convenience

ICON_NAMES = {
'module': 'xapp-prefs-plugins-symbolic',
'class': 'application-x-appliance-symbolic',
'instance': 'insert-object-symbolic',
'function': 'system-run-symbolic',
'param': 'dialog-question-symbolic',
'path': 'inode-directory-symbolic',
'keyword': 'insert-text-symbolic',
'property': 'document-properties-symbolic',
'statement': 'document-send-symbolic',
}

class GediPlugin(GObject.Object, Gedit.ViewActivatable):
__gtype_name__ = "GediPlugin"
py_extension = ".py"
view = GObject.property(type=Gedit.View)

def __init__(self):
GObject.Object.__init__(self)
self.completion_provider = None

def do_activate(self):
print("Gedi is activated.")
document = self.view.get_buffer()
document.connect("loaded", self.on_document_load)

if document.get_uri_for_display().endswith(self.py_extension):
self.completion_provider = GediCompletionProvider()
self.view.get_completion().add_provider(self.completion_provider)

def do_deactivate(self):
print("Gedi is deactivated.")

def on_document_load(self, document, p3=None, p4=None, p5=0, p6=0):
if document.get_uri_for_display().endswith(self.py_extension):
if self.completion_provider is None:
self.completion_provider = GediCompletionProvider()
self.view.get_completion().add_provider(self.completion_provider)
else:
if self.completion_provider is not None:
self.view.get_completion().remove_provider(self.completion_provider)
self.completion_provider = None

view = GObject.property(type=Gedit.View)

def __init__(self):
super().__init__()
self.completion_provider = None
self.signal = None

def do_activate(self):
document = self.view.get_buffer()
self.signal = document.connect('loaded', self.on_document_load)
self.on_document_load(document)

def do_deactivate(self):
if (self.signal is not None):
document = self.view.get_buffer()
document.disconnect(self.signal)
self.signal = None

def on_document_load(self, document, *_):
if ((lang := document.get_language()) and 'python' in lang.get_name().casefold()):
self.completion_provider = GediCompletionProvider(document)
self.view.get_completion().add_provider(self.completion_provider)
else:
if (self.completion_provider is not None):
self.view.get_completion().remove_provider(self.completion_provider)
self.completion_provider = None

class GediCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
__gtype_name__ = 'GediProvider'

def __init__(self):
GObject.Object.__init__(self)

def do_get_name(self):
return _("Gedi Python Code Completion")

def get_iter_correctly(self, context):
if isinstance(context.get_iter(), tuple):
return context.get_iter()[1];
else:
return context.get_iter()

def do_match(self, context):
iter = self.get_iter_correctly(context)
iter.backward_char()
buffer=iter.get_buffer()
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
return False
ch = iter.get_char()
if not (ch in ('_', '.') or ch.isalnum()):
return False

return True

def do_get_priority(self):
return 1

def do_get_activation(self):
return GtkSource.CompletionActivation.INTERACTIVE

def do_populate(self, context):
#TODO: do async maybe?
it = self.get_iter_correctly(context)
document = it.get_buffer()
proposals = []

for completion in Jedi.get_script(document).completions():
complete = completion.name
if jedi.__version__ <= (0,7,0):
doc=completion.doc
else:
doc=completion.docstring()
proposals.append(GtkSource.CompletionItem.new(completion.name,
completion.name,
self.get_icon_for_type(completion.type),
doc))


context.add_proposals(self, proposals, True)

def get_icon_for_type(self, _type):
theme = Gtk.IconTheme.get_default()
try:
return theme.load_icon(icon_names[_type.lower()], 16, 0)
except:
try:
return theme.load_icon(Gtk.STOCK_ADD, 16, 0)
except:
return None


GObject.type_register(GediCompletionProvider)
name = "Code completion"
priority = 1

def __init__(self, document):
super().__init__()
self.document = document
self.icon = GdkPixbuf.Pixbuf.new_from_file_at_scale(os.path.join(os.path.dirname(__file__), 'logo.png'), 16, 16, True)
self.populate_thread_ident = None
self.populate_thread_lock = threading.Lock()

def do_get_activation(self):
return GtkSource.CompletionActivation.INTERACTIVE

def do_get_icon(self):
return self.icon

def do_get_info_widget(self, proposal):
buffer = GtkSource.Buffer(
highlight_matching_brackets = False,
language = self.document.get_language(),
style_scheme = self.document.get_style_scheme(),
)

widget = GtkSource.View(
buffer = buffer,
can_focus = False,
cursor_visible = False,
editable = False,
highlight_current_line = False,
sensitive = False,
)

widget.show()

return widget

def do_get_name(self):
return _(self.name)

def do_get_priority(self):
return self.priority

def do_match(self, context):
iter = context.get_iter()[1]
iter.backward_char()
if ('no-spell-check' not in self.document.get_context_classes_at_iter(iter)): return False
ch = iter.get_char()
return (ch in '_.' or ch.isalnum())

def do_populate(self, context):
# do our best to prevent previous thread to call `idle_add()`
self.populate_thread_ident = None
with self.populate_thread_lock:
self.populate_thread_ident = None

code = self.document.get_text(self.document.get_start_iter(), self.document.get_end_iter(), False)
path = (loc.get_path() if (loc := self.document.get_file().get_location()) else '')
iter_cursor = self.document.get_iter_at_mark(self.document.get_insert())
linenum = iter_cursor.get_line()+1
charnum = iter_cursor.get_line_index()

with self.populate_thread_lock:
t = threading.Thread(target=self.populate, args=(context, code, path, linenum, charnum), daemon=True)
t.start()
self.populate_thread_ident = t.ident

def do_update_info(self, proposal, info):
widget = info.get_child()
buffer = widget.get_buffer()
buffer.set_text(proposal.get_info())

def populate(self, context, code: str, path: str, linenum: int, charnum: int):
script = jedi.Script(code=code, path=path, environment=jedi.api.environment.InterpreterEnvironment())
completions = script.complete(linenum, charnum, fuzzy=False) # TODO: add setting for `fuzzy`

proposals = [GtkSource.CompletionItem(
label = i.name,
text = i.name_with_symbols,
icon_name = ICON_NAMES.get(i.type.casefold(), Gtk.STOCK_ADD),
info = i.docstring(),
) for i in completions]

ident = threading.get_ident()
with self.populate_thread_lock:
if (ident == self.populate_thread_ident):
GLib.idle_add(context.add_proposals, self, proposals, True)
104 changes: 104 additions & 0 deletions gedi.py.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env python3

import os, tempfile, subprocess
from jedi.api import Script
from gi.repository import Gtk, Gedit, GObject, GtkSource

# FIXME: find real icon names
icon_names = {
'import': '',
'module': '',
'class': '',
'function': '',
'statement': '',
'param': '',
}

class Jedi:
def get_script(document):
doc_text = document.get_text(document.get_start_iter(), document.get_end_iter(), False)
loc = document.get_file().get_location()

return Script(code=doc_text, path=loc.get_path() if (loc) else '')

class GediPlugin(GObject.Object, Gedit.ViewActivatable):
__gtype_name__ = 'GediPlugin'

view = GObject.property(type=Gedit.View)

def __init__(self):
super().__init__()
self.completion_provider = None

def do_activate(self):
print("Gedi is activated.")

document = self.view.get_buffer()
document.connect("loaded", self.on_document_load)

if ((lang := document.get_language()) and 'python' in lang.get_name().casefold()):
self.completion_provider = GediCompletionProvider()
self.view.get_completion().add_provider(self.completion_provider)

def do_deactivate(self):
print("Gedi is deactivated.")

def on_document_load(self, document, p3=None, p4=None, p5=0, p6=0):
if ((lang := document.get_language()) and 'python' in lang.get_name().casefold()):
if (self.completion_provider is None):
self.completion_provider = GediCompletionProvider()
self.view.get_completion().add_provider(self.completion_provider)
else:
if (self.completion_provider is not None):
self.view.get_completion().remove_provider(self.completion_provider)
self.completion_provider = None

@GObject.type_register
class GediCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
__gtype_name__ = 'GediProvider'

def __init__(self):
super().__init__()

def do_get_name(self):
return _("Gedi Python Code Completion")

def get_iter_correctly(self, context):
return (context.get_iter()[1] if (isinstance(context.get_iter(), tuple)) else context.get_iter())

def do_match(self, context):
iter = self.get_iter_correctly(context)
iter.backward_char()
buffer = iter.get_buffer()
if (buffer.get_context_classes_at_iter(iter) != ['no-spell-check']): return False
ch = iter.get_char()
return (ch in ('_', '.') or ch.isalnum())

def do_get_priority(self):
return 1

def do_get_activation(self):
return GtkSource.CompletionActivation.INTERACTIVE

def do_populate(self, context): # TODO: do async maybe?
it = self.get_iter_correctly(context)
document = it.get_buffer()
proposals = []

iter_cursor = document.get_iter_at_mark(document.get_insert())
linenum = iter_cursor.get_line() + 1
charnum = iter_cursor.get_line_index()

for completion in Jedi.get_script(document).complete(linenum, charnum):
complete = completion.name
doc = completion.docstring()
proposals.append(GtkSource.CompletionItem(label=completion.name, text=completion.name, icon=self.get_icon_for_type(completion.type), info=doc))

context.add_proposals(self, proposals, True)

def get_icon_for_type(self, _type):
theme = Gtk.IconTheme.get_default()
try: return theme.load_icon(icon_names[_type.lower()], 16, 0)
except Exception:
try: return theme.load_icon(Gtk.STOCK_ADD, 16, 0)
except Exception: return None
Binary file added logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.