Skip to content

Instantly share code, notes, and snippets.

@sublimator
Created September 8, 2011 11:53
Show Gist options
  • Save sublimator/1203231 to your computer and use it in GitHub Desktop.
Save sublimator/1203231 to your computer and use it in GitHub Desktop.
Updated Indentation
import sublime
import sublime_plugin
import textwrap
def get_tab_size(view):
return int(view.settings().get('tab_size', 8))
def handle_tabs(view, string, offset=0):
if not view.settings().get('translateTabsToSpaces'):
tab_size = view.settings().get('tab_size', 8)
string = process_indentation(string, tab_size, offset)
return string
def insert_or_replace(edit, view, region, string):
if region:
a = view.replace(edit, region, string)
else:
a = view.insert(edit, region.begin(), string)
return a
def insert_or_replace_indented(edit, view, region, string):
string = string.splitlines(1)
cursor_at = normed_indentation_pt(view, region)
indent = cursor_at * ' '
indented_string = ''.join([
handle_tabs(view, l) for l in
([ string[0] ] + [ indent + l for l in string[1:] ]) ])
return insert_or_replace(edit, view, region, indented_string)
def normed_indentation_pt(view, sel, non_space=False):
tab_size = get_tab_size(view)
pos = 0
ln = view.line(sel)
for pt in xrange(ln.begin(), ln.end() if non_space else sel.begin()):
ch = view.substr(pt)
if ch == '\t':
pos += tab_size - (pos % tab_size)
elif ch.isspace():
pos += 1
elif non_space:
break
else:
pos+=1
return pos
def line_and_normed_pt(view, pt):
return ( view.rowcol(pt)[0],
normed_indentation_pt(view, sublime.Region(pt)) )
def pt_from_line_and_normed_pt(view, (ln, pt)):
i = start_pt = view.text_point(ln, 0)
tab_size = get_tab_size(view)
pos = 0
for i in xrange(start_pt, start_pt + pt):
ch = view.substr(i)
if ch == '\t':
pos += tab_size - (pos % tab_size)
else:
pos += 1
i += 1
if pos == pt: break
return i
def save_selections(view, selections=None):
return [ [line_and_normed_pt(view, p) for p in (sel.a, sel.b)]
for sel in selections or view.sel() ]
def region_from_stored_selection(view, stored):
return sublime.Region(*[pt_from_line_and_normed_pt(view, p) for p in stored])
def restore_selections(view, lines_and_pts):
view.sel().clear()
for stored in lines_and_pts:
view.sel().add(region_from_stored_selection(view, stored))
def compress_column(column):
# "SS\T"
if all(c.isspace() for c in column):
column = '\t'
# "CCSS"
elif column[-1] == ' ':
while column and column[-1] == ' ':
column.pop()
column.append('\t')
# "CC\T"
return column
def process_indentation( the_string, tab_size, first_line_offset = 0,
only_leading=True, process_column = compress_column):
lines = the_string.split('\n')
compressed = []
for li, line in enumerate(lines):
pos = 0
if not li: pos += first_line_offset
rebuilt_line = []
column = []
for i, char in enumerate(line):
if only_leading and not char.isspace():
column.extend(line[i:])
break
column.append(char)
pos += 1
if char == '\t':
pos += tab_size - (pos % tab_size)
if pos % tab_size == 0:
rebuilt_line.extend(process_column(column))
column = []
rebuilt_line.extend(column)
compressed.append(''.join(rebuilt_line))
return '\n'.join(compressed)
class TabCommand(sublime_plugin.TextCommand):
def run(self, edit, whole_buffer = True,
only_leading = True,
translate = None,
# ignore set_translate_tabs that was always set `true`
**kw ):
view = self.view
view.settings().set('translate_tabs_to_spaces', (
translate if translate is not None else self.translate ))
if whole_buffer or not view.has_non_empty_selection_region():
self.operation_regions = [sublime.Region(0, view.size())]
else:
self.operation_regions = view.sel()
sels = save_selections(view)
visible, = save_selections(view, [view.visible_region()])
self.do(view, edit, only_leading)
restore_selections(view, sels)
visible = region_from_stored_selection(view, visible)
view.show(visible, False)
view.run_command("scroll_lines", {"amount": 1.0 })
def do(self, view, edit, only_leading):
tab_size = self.tab_size = get_tab_size(view)
for sel in self.operation_regions:
the_string = view.substr(sel)
first_line_off_set = normed_indentation_pt( view, sel ) % tab_size
compressed = process_indentation (
the_string, tab_size, first_line_off_set,
only_leading = only_leading, process_column=self.process_column)
view.replace(edit, sel, compressed)
class ExpandTabs(TabCommand):
translate = True
process_column = lambda s, l: ''.join(l).expandtabs(s.tab_size)
class UnexpandTabs(TabCommand):
translate = False
process_column = staticmethod(compress_column)
class RelativeIndent(sublime_plugin.TextCommand):
is_enabled = lambda s, **a: s.view.sel()
def run(self, ed, cmd='paste'):
view = self.view
sels = view.sel()
if cmd == 'paste':
clip = sublime.get_clipboard().expandtabs(get_tab_size(view))
clip = textwrap.dedent(clip).rstrip().split(u'\n')
if len(sels) > 1: # Columnar Paste
for region in view.sel():
insert_or_replace(ed, view, region, clip.pop(0))
else:
clip = '\n'.join(clip)
insert_or_replace_indented(ed, view, view.sel()[0], clip)
else:
clip = []
for sel in view.sel():
indent = normed_indentation_pt(view, sel) * ' '
lines = view.substr(sel).splitlines(1)
clip.extend([indent + lines[0]] + lines[1:])
sublime.set_clipboard(''.join(clip))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment