Skip to content

Instantly share code, notes, and snippets.

@chergert
Created March 19, 2011 01:33
Show Gist options
  • Save chergert/877130 to your computer and use it in GitHub Desktop.
Save chergert/877130 to your computer and use it in GitHub Desktop.
try to get spacing/formatting right for my C style with gtksourceview
#!/usr/bin/env python
"""
A simple editor built for live editing in GLib'ish manner.
"""
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import GtkSource
from gi.repository import Pango
def getLastCharacter(iter, char, syncChar=None):
"""
Looks for last occurance of @char. If @syncChar is found, an equal
number of @char characters will be skipped.
"""
toSkip = 0
iter = iter.copy()
while iter.backward_char():
if iter.get_char() == syncChar:
toSkip += 1
if iter.get_char() == char:
if toSkip:
toSkip -= 1
else:
return iter.get_line(), iter.get_line_offset()
return -1, -1
class AutoIndenter(GObject.Object):
__gtype_name__ = 'GbAutoIndenter'
def __init__(self, sourceView):
GObject.Object.__init__(self)
self.sourceView = sourceView
self.sourceBuffer = sourceView.get_buffer()
self.sourceBuffer.connect('insert-text', self.insertText)
def insertText(self,
sourceBuffer,
insertLocation,
insertText,
insertTextLenght,
userData=None):
"""
Handle insertion of text into the source buffer.
If the inserted text is a newline, then we will try to determine
where the next line of text should begin and properly space to
that position.
"""
line = insertLocation.get_line()
if insertText == '\n':
insertLocation.forward_chars(len(insertText) - 1)
predent, postdent = self.getIndentText(insertLocation.get_line())
sourceBuffer.insert(insertLocation, predent)
origLine = insertLocation.get_line()
origLineOffset = insertLocation.get_line_offset()
sourceBuffer.insert(insertLocation, postdent)
insertLocation.set_line(origLine)
insertLocation.set_line_offset(origLineOffset)
elif insertText == '}':
line = insertLocation.get_line()
if not self.getLineText(line).strip():
line, offset = getLastCharacter(insertLocation, '{', '}')
if line >= 0 and offset >= 0:
predent = ''
lineText = self.getLineText(line)
for i in lineText:
if i in (' ', '\t'):
predent += i
continue
break
startIter = insertLocation.copy()
startIter.set_line_offset(0)
sourceBuffer.delete(startIter, insertLocation)
sourceBuffer.insert(insertLocation, predent)
elif insertText == '/':
iter = insertLocation.copy()
iter.backward_char()
if iter.get_char() == ' ':
iter.backward_char()
if iter.get_char() == '*':
iter.forward_char()
sourceBuffer.delete(iter, insertLocation)
def getLineText(self, line):
if line < 0:
return ""
startIter = self.sourceBuffer.get_iter_at_line(line)
endIter = startIter.copy()
endIter.forward_to_line_end()
return self.sourceBuffer.get_slice(startIter, endIter, True)
def lineEndsDeclaration(self, line):
lineText = self.getLineText(line)
if lineText.strip().endswith(')'):
# Get the line that started this paranthesis
iter = self.sourceBuffer.get_iter_at_line(line)
iter.forward_to_line_end()
iter.backward_char()
cLine, cOffset = getLastCharacter(iter, '(', ')')
if cLine >= 0 and cOffset >= 0:
# If this line has content at offset 0, it is a function
cText = self.getLineText(cLine)
if cText[0] not in (' ', '\t'):
return True
def getIndentText(self, line):
"""
Retrieves the indentation text for the next line. Returns a two-part
tuple containing the data to add before the insertion and the data
to insert after the insertion.
For example, a tuple of:
('\n{', '\t\n}')
would indicate that adding { and tabbing in one level.
"""
if self.lineEndsDeclaration(line):
return ('\n{', '\t')
lineText = self.getLineText(line)
if lineText.strip() == '*/':
return ('', lineText[:lineText.index('*') - 1])
if lineText.endswith(','):
if '(' in lineText:
predent = ''
iter = self.sourceBuffer.get_iter_at_line(line)
iter.forward_to_line_end()
cLine, cOffset = getLastCharacter(iter, '(', ')')
cLineText = self.getLineText(cLine)
for i in cLineText:
if i in (' ', '\t'):
predent += i
else:
break
predent += ' ' * cOffset
if cLine == line:
predent += ' '
return ('', predent)
if lineText.strip()[:2] in ('*', '* ', '/*'):
return ('', lineText[:lineText.index('*')].replace('/', ' ') + '* ')
o = 0
for i in lineText:
if i not in (' ', '\t'):
add = ''
if lineText.endswith('{') \
and not lineText.strip().startswith('switch '):
add = '\t'
elif lineText.endswith(':') \
and lineText.strip().startswith('case'):
add = '\t'
return ('', lineText[:o] + add)
o += 1
return ('', lineText[:o])
class FunctionStyler(GObject.Object):
__gtype_name__ = 'GbFunctionStyler'
def __init__(self, sourceView):
GObject.Object.__init__(self)
self.sourceView = sourceView
self.sourceBuffer = sourceView.get_buffer()
self.sourceBuffer.connect('insert-text', self.insertText)
def insertText(self,
sourceBuffer,
insertLocation,
insertText,
insertTextLenght,
userData=None):
if insertText == ')':
line, offset = getLastCharacter(insertLocation, '(', ')')
if line >= 0 and offset >= 0:
startIter = sourceBuffer.get_iter_at_line_offset(line, offset)
startIter.forward_char()
text = sourceBuffer.get_slice(startIter, insertLocation, True)
args = self.parseFuncDeclArgs(text)
if args:
self.reformatFuncDeclArgs(startIter, insertLocation, args)
def reformatFuncDeclArgs(self, startIter, endIter, args):
longestType = max([len(a) for a,b,c in args])
longestDeref = max([len(b) for a,b,c in args])
parts = []
if longestDeref > 0:
formatString = '%%-%ds %%%ds%%s' % (longestType, longestDeref)
for arg in args:
parts.append(formatString % arg)
else:
formatString = '%%-%ds %%s' % longestType
for a,b,c in args:
parts.append(formatString % (a, c))
offset = startIter.get_line_offset()
self.sourceBuffer.delete(startIter, endIter)
space = ' ' * offset
text = (',\n' + space).join(parts)
self.sourceBuffer.insert(endIter, text)
def parseFuncDeclArgs(self, text):
try:
args = []
parts = [a.strip() for a in text.split(',')]
for part in parts:
tn = ''
dr = ''
nm = ''
if '*' in part:
tn_and_dr = part[:part.rindex('*') + 1]
nm = part[part.rindex('*') + 1:]
else:
t = part.replace('\t', ' ')
tn_and_dr, nm = [a.strip() for a in t.split(' ', 2)]
dr = '*' * tn_and_dr.count('*')
tn = tn_and_dr.replace('*','')
args.append((tn.strip(), dr.strip(), nm.strip()))
if len(args):
return args
except Exception, err:
print err
if __name__ == '__main__':
w = Gtk.Window()
w.props.title = 'ChergsEdit'
w.props.window_position = Gtk.WindowPosition.CENTER
w.set_default_size(700, 800)
s = Gtk.ScrolledWindow(visible=True)
w.add(s)
lm = GtkSource.LanguageManager.get_default()
cm = GtkSource.StyleSchemeManager.get_default()
b = GtkSource.Buffer(language=lm.get_language("c"),
style_scheme=cm.get_scheme("tango"))
v = GtkSource.View(**{
'visible': True,
'buffer': b,
'auto-indent': False,
'indent-width': 4,
'tab-width': 4,
'right-margin-position': 80,
'highlight-current-line': True,
'show-line-numbers': True,
'show-right-margin': True,
})
s.add(v)
ai = AutoIndenter(v)
fs = FunctionStyler(v)
fd = Pango.FontDescription()
fd.set_family("Monospace")
fd.set_size(Pango.SCALE * 10)
v.override_font(fd)
w.connect('delete-event', lambda *_: Gtk.main_quit())
w.present()
Gtk.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment