"""""""""""""""""""""""""""""""""""" Commands """"""""""""""""""""""""""""""""""
command! -nargs=0 ShrVimTryUsePython3 call _ShrVimTryUsePython3(1)
command! -nargs=0 ShrVimTryUsePython2 call _ShrVimTryUsePython2(1)
command! -nargs=+ ShrVimConnect call _ShrVimCallPythonFunc('connect', [<f-args>])
command! -nargs=0 ShrVimDisconnect call _ShrVimCallPythonFunc('disconnect', [])
command! -nargs=0 ShrVimSync call _ShrVimCallPythonFunc('sync', [])
command! -nargs=0 ShrVimShowInfo call _ShrVimCallPythonFunc('show_info', [])
command! -nargs=1 ShrVimSetTimeout call _ShrVimCallPythonFunc('set_bvar', ['TIMEOUT', <f-args>])
command! -nargs=1 ShrVimSetNumOfGroups call _ShrVimCallPythonFunc('set_bvar', ['NUM_GROUPS', <f-args>])
""""""""""""""""""""""""""""""""""""" Setup """"""""""""""""""""""""""""""""""""
" Highlight for other users.
for i in range(0, 100)
exec 'hi ShrVimNor' . i . ' ctermbg=darkyellow'
exec 'hi ShrVimIns' . i . ' ctermbg=darkred'
exec 'hi ShrVimVbk' . i . ' ctermbg=darkblue'
endfor
let g:shrvim_auto_sync_level = 3
" Auto commands
autocmd! InsertLeave * call _ShrVimAutoSync(1)
autocmd! CursorMoved * call _ShrVimAutoSync(1)
autocmd! CursorHold * call _ShrVimAutoSync(1)
autocmd! CursorMovedI * call _ShrVimAutoSync(3)
autocmd! CursorHoldI * call _ShrVimAutoSync(3)
""""""""""""""""""""""""""""""""""" Functions """"""""""""""""""""""""""""""""""
function! _ShrVimTryUsePython3(show_err)
if has('python3')
command! -nargs=* ShrVimPython python3 <args>
call _ShrVimSetup()
return 1
else
if a:show_err
echoerr 'Sorry, :python3 is not supported in this version'
endif
return 0
endif
endfunction
function! _ShrVimTryUsePython2(show_err)
if has('python')
command! -nargs=* ShrVimPython python <args>
call _ShrVimSetup()
return 1
else
if a:show_err
echoerr 'Sorry, :python is not supported in this version'
endif
return 0
endif
endfunction
function! _ShrVimCallPythonFunc(func_name, args)
if exists('g:shrvim_setupped') && g:shrvim_setupped == 1
if len(a:args) == 0
exe 'ShrVimPython ' . a:func_name . '()'
else
let args_str = '"' . join(a:args, '", "') . '"'
exe 'ShrVimPython ' . a:func_name . '(' . args_str . ')'
endif
endif
endfunction
function! _ShrVimAutoSync(level)
let level = g:shrvim_auto_sync_level
if exists('b:shrvim_auto_sync_level')
let level = b:shrvim_auto_sync_level
endif
if a:level <= level
ShrVimSync
endif
endfunction
function! _ShrVimSetup()
ShrVimPython << EOF
# python << EOF
# ^^ Force vim highlighting the python code below.
import json
import socket
import sys
import vim
if sys.version_info[0] == 3:
unicode = str
class CURSOR_MARK: # pylint:disable=W0232
"""Enumeration type of cursor marks."""
CURRENT = '.'
V = 'v'
class MATCH_PRI: # pylint:disable=W0232
"""Enumerations types of match priority."""
NORMAL = 2
INSERT = 3
VISUAL = 1
class MODE: # pylint:disable=W0232
"""Enumeration type of mode."""
NORMAL = 1 # normal mode.
INSERT = 2 # insert mode.
REPLACE = 3 # replace mode.
VISUAL = 4 # visual mode.
LINE_VISUAL = 5 # line visual mode.
BLOCK_VISUAL = 6 # block visual mode.
class VARNAMES: # pylint: disable=W0232
"""Enumeration types of variable name in vim."""
GROUP_NAME_PREFIX = 'gnp_' # Group name's prefix.
IDENTITY = 'identity' # Identity of the user.
INIT = 'init' # Initial or not.
LINES = 'lines' # Lines.
NUM_GROUPS = 'num_groups' # Number of groups.
SERVER_NAME = 'server_name' # Server name.
SERVER_PORT = 'port' # Server port.
TIMEOUT = 'timeout' # Timeout for TCPConnection.
USERS = 'users' # List of users.
# Name of the normal cursor group.
NORMAL_CURSOR_GROUP = lambda x: 'ShrVimNor%d' % x
# Name of the insert cursor group.
INSERT_CURSOR_GROUP = lambda x: 'ShrVimIns%d' % x
# Name of the visual group.
VISUAL_GROUP = lambda x: 'ShrVimVbk%d' % x
DEFAULT_TIMEOUT = 1
DEFAULT_NUM_GROUPS = 5
class JSON_TOKEN: # pylint:disable=W0232
"""Enumeration the Ttken strings for json object."""
BYE = 'bye' # Resets the user and do nothong.
CURSORS = 'cursors' # other users' cursor position
DIFF = 'diff' # Difference between this time and last time.
ERROR = 'error' # error string
IDENTITY = 'identity' # identity of myself
INIT = 'init' # initialize connect flag
MODE = 'mode' # vim mode.
NICKNAME = 'nickname' # nick name of the user.
OTHERS = 'others' # other users info.
############### Handler for Variable stores only in python #####################
class ScopeVars(object):
"""A scoped variables handler.
Attributes:
_curr_scope: Current scope number.
_vars: A dict contains all scope's variables and the values.
"""
def __init__(self):
"""Constructor."""
self._curr_scope = None
self._vars = {}
@property
def curr_scope(self):
"""Gets the current scope number."""
return self._curr_scope
@curr_scope.setter
def curr_scope(self, value):
"""Sets the current scope number."""
self._curr_scope = value
self._vars.setdefault(self._curr_scope, {})
def get(self, variable_name, default=None):
"""Gets the specified variable.
Args:
variable_name: Name of the variable to get.
default: The default value to return if the variable is not exist.
Return:
The value.
"""
return self._vars[self._curr_scope].get(variable_name, default)
def __getitem__(self, variable_name):
"""Gets the specified variable.
Args:
variable_name: Name of the variable to get.
Return:
The value.
"""
return self._vars[self._curr_scope][variable_name]
def __setitem__(self, variable_name, value):
"""Sets the specifiec variable.
Args:
variable_name: Name of the variable to set.
value: The new value.
"""
self._vars[self._curr_scope][variable_name] = value
def __delitem__(self, variable_name):
"""Deletes the specifiec variable.
Args:
variable_name: Name of the variable to delete.
"""
del self._vars[self._curr_scope][variable_name]
def __contains__(self, variable_name):
"""Checks whether the variable is exist or not.
Args:
variable_name: Name of the variable to check.
Return:
True if the variable exists; otherwise, False.
"""
return variable_name in self._vars[self._curr_scope]
# For scope=buffer
py_bvars = ScopeVars()
# For scope=window
py_wvars = ScopeVars()
############################### Interface to vim ###############################
class VimCursorsInfo(object): # pylint: disable=W0232
"""Gets/sets the cursor position in vim."""
def __getitem__(self, mark): # pylint: disable=R0201
"""Gets the cursor position with specifying the mark.
Args:
mark: Mark of the cursor to get.
Return:
Cursor position.
"""
pos = [int(x) for x in vim.eval('getpos("%s")' % mark)]
row = pos[1] - 1
line = VimInfo.transform_to_vim(VimInfo.lines[row])
col = len(VimInfo.transform_to_py(line[ : pos[2] - 1]))
return (row, col)
def __setitem__(self, mark, rc): # pylint: disable=R0201
"""Sets the cursor position with specifying the mark.
Args:
mark: Mark of the cursor to set.
rc: The new cursor position.
"""
row, col = self.transform_to_vim(rc)
mark = mark if mark != CURSOR_MARK.V else CURSOR_MARK.CURRENT
vim.eval('setpos("%s", [0, %d, %d, 0])' % (mark, row + 1, col + 1))
def transform_to_vim(self, rc): # pylint: disable=R0201
"""Transform rc in utf-8 to rc in bytes.
Args:
rc: Cursor position.
Return:
Cursor position.
"""
row = rc[0]
line = VimInfo.lines[row] if row < len(VimInfo.lines) else ''
col = len(VimInfo.transform_to_vim(line[ : rc[1]]))
return (row, col)
class GroupInfo(object):
"""Higilights informations about a user group.
Attributes:
_normal_cursor_ranges: List of highlight block ranges in normal mode.
_insert_cursor_ranges: List of highlight block ranges in insert mode.
_visual_ranges: List of highlight block ranges in visual blocks.
"""
def __init__(self):
"""Constructor."""
self._normal_cursor_ranges = []
self._insert_cursor_ranges = []
self._visual_ranges = []
def set_mode_cursor(self, mode, rc):
"""Sets the mode and the cursor.
Args:
mode: The mode.
rc: The cursor position.
"""
if mode in (MODE.INSERT, MODE.REPLACE):
self._insert_cursor_ranges.append((rc[0], rc[1], rc[1] + 1))
else:
self._normal_cursor_ranges.append((rc[0], rc[1], rc[1] + 1))
def add_visual(self, rang):
"""Add a visual position.
Args:
rang: The position.
"""
self._visual_ranges += [rang]
@property
def normal_cursor_ranges(self):
"""Gets the normal cursor ranges."""
return self._normal_cursor_ranges
@property
def insert_cursor_ranges(self):
"""Gets the insert cursor ranges."""
return self._insert_cursor_ranges
@property
def visual_ranges(self):
"""Gets the visual ranges."""
return self._visual_ranges
class VimHighlightInfo(object):
"""Highlight informations about users.
Attributes:
_groups: A list of instance of GroupInfo.
_username_to_group: A dict for mapping the username to the instance of
GroupInfo.
"""
def __init__(self):
"""Constructor."""
self._groups = None
self._username_to_group = {}
def __getitem__(self, name):
"""Gets the group (a instance of GroupInfo) by nickname.
Args:
name: Nick name.
Return:
The group in which the user is.
"""
return self._username_to_group[name]
def reset(self, nicknames):
"""Reset the users.
Args:
nicknames: A list of nickname.
"""
self._groups = [GroupInfo() for _ in range(self.num_of_groups())]
self._username_to_group = {name : self._groups[self._get_group_id(name)]
for name in nicknames}
def render(self):
"""Render the highlight to vim."""
for index in range(self.num_of_groups()):
VimInfo.match(NORMAL_CURSOR_GROUP(index), MATCH_PRI.NORMAL,
self._groups[index].normal_cursor_ranges)
VimInfo.match(INSERT_CURSOR_GROUP(index), MATCH_PRI.INSERT,
self._groups[index].insert_cursor_ranges)
VimInfo.match(VISUAL_GROUP(index), MATCH_PRI.VISUAL,
self._groups[index].visual_ranges)
def _get_group_id(self, string):
"""Transform the gived string to a valid group index.
Args:
string: The gived string.
Return:
The index in [0, number of groups)
"""
x = 0
for c in string:
x = (x * 23 + ord(c)) % self.num_of_groups()
return x
@staticmethod
def num_of_groups():
"""Gets the number of groups."""
return py_bvars.get(VARNAMES.NUM_GROUPS, DEFAULT_NUM_GROUPS)
class VimLinesInfo(object):
"""An interface for accessing the vim's buffer.
Attributes:
_lines: An list of lines, which is encoded text got from vim.buffer.
"""
def __init__(self):
"""Constructor."""
self._lines = [VimInfo.transform_to_py(line)
for line in vim.current.buffer]
def __getitem__(self, index):
"""Get a specified line.
Args:
index: The line number.
"""
return self._lines[index]
def __setitem__(self, index, text):
"""Sets a specified line.
Args:
index: The line number.
text: The new text.
"""
self._lines[index] = text
if isinstance(index, slice):
lines = [VimInfo.transform_to_vim(line) for line in text]
if index.start is not None and index.stop is not None:
vim.current.buffer[index.start : index.stop] = lines
elif index.start is None and index.stop is not None:
vim.current.buffer[ : index.stop] = lines
elif index.start is not None and index.stop is None:
vim.current.buffer[index.start : ] = lines
else:
vim.current.buffer[:] = lines
else:
vim.current.buffer[index] = VimInfo.transform_to_vim(text)
def __len__(self):
"""Gets the number of rows."""
return len(self._lines)
def gen_patch(self, orig_lines):
"""Creates a patch from an old one.
Args:
orig_lines: Original lines of the text.
Return:
A list of replacing information.
"""
orig_rows, new_rows = len(orig_lines), len(self._lines)
for first in range(min(orig_rows, new_rows)):
if orig_lines[first] != self._lines[first]:
break
else:
if orig_rows < new_rows:
return [(orig_rows, orig_rows, self._lines[orig_rows : ])]
elif orig_rows > new_rows:
return [(first + 1, orig_rows, [])]
else:
return []
delta = new_rows - orig_rows
for last in range(orig_rows - 1, first - 1, -1):
if orig_lines[last] != self._lines[last + delta]:
break
else:
last -= 1
return [(first, last + 1, self._lines[first : last + delta + 1])]
def apply_patch(self, patch_info):
"""Applies a patch.
Args:
patch_info: A list of replacing information.
Return:
A list of text.
"""
offset = 0
for beg, end, lines in patch_info:
self.__setitem__(slice(beg + offset, end + offset), lines)
offset += len(lines) - (end - beg)
class VimInfoMeta(type):
"""An interface for accessing the vim's vars, buffer, cursors, etc.
Static attributes:
cursors: An instance of VimCursorsInfo, for accessing the cursor
information in vim.
highlight: An instance of VimHighlightInfo, for accessing the
information about highlight in vim.
ENCODING: vim's encoding.
"""
cursors = VimCursorsInfo()
highlight = VimHighlightInfo()
ENCODING = vim.eval('&encoding')
def __init__(self, *args):
"""Constructor."""
self._mode = None
self._lines = None
def init(self):
"""Initialize informations for this time."""
self._lines = VimLinesInfo()
@property
def lines(self):
"""Gets list of lines in the buffer."""
return self._lines
@property
def mode(self):
"""Gets the current mode."""
mode_str = vim.eval('mode()')
if mode_str == 'i':
self._mode = MODE.INSERT
elif mode_str == 'R':
self._mode = MODE.REPLACE
elif mode_str == 'v':
self._mode = MODE.VISUAL
elif mode_str == 'V':
self._mode = MODE.LINE_VISUAL
elif len(mode_str) == 1 and ord(mode_str) == 22:
self._mode = MODE.BLOCK_VISUAL
else:
self._mode = MODE.NORMAL
return self._mode
@mode.setter
def mode(self, value):
"""Sets the current mode."""
if self._mode != value:
if value == MODE.INSERT:
vim.command('startinsert')
elif value == MODE.REPLACE:
vim.command('startreplace')
elif value == MODE.VISUAL:
vim.command('exe "norm! v"')
elif value == MODE.LINE_VISUAL:
vim.command('exe "norm! V"')
elif value == MODE.BLOCK_VISUAL:
vim.command('exe "norm! %c"' % 22)
else:
vim.command('exe "norm! \\<esc>"')
self._mode = value
@staticmethod
def match(group_name, priority, positions):
"""Set the match informations.
Args:
group_name: Group name.
priority: Priority for the vim function matchadd().
positions: List of row-column position.
"""
last_id = py_wvars.get(VARNAMES.GROUP_NAME_PREFIX + group_name, None)
if last_id is not None and last_id > 0:
vim.eval('matchdelete(%d)' % last_id)
del py_wvars[VARNAMES.GROUP_NAME_PREFIX + group_name]
if positions:
rcs = []
for row, col_beg, col_end in positions:
col_beg = VimInfo.cursors.transform_to_vim((row, col_beg))[1]
col_end = VimInfo.cursors.transform_to_vim((row, col_end))[1]
rcs += [(row, col) for col in range(col_beg, col_end)]
patterns = '\\|'.join(['\\%%%dl\\%%%dc' % (rc[0] + 1, rc[1] + 1)
for rc in rcs])
mid = int(vim.eval("matchadd('%s', '%s', %d)" %
(group_name, patterns, priority)))
if mid != -1:
py_wvars[VARNAMES.GROUP_NAME_PREFIX + group_name] = mid
@staticmethod
def transform_to_py(data):
"""Transforms the data to python's data type if needs.
In python, we always use unicode(python2)/str(python3) instead of bytes.
Args:
data: The data to be transform.
Return:
Data which are transformed.
"""
return (data if not isinstance(data, bytes)
else data.decode(VimInfo.ENCODING))
@staticmethod
def transform_to_vim(data):
"""Transforms the data to vim's data type if needs.
Args:
data: The data to be transform.
Return:
Data which are transformed.
"""
return (data if not isinstance(data, unicode)
else data.encode(VimInfo.ENCODING))
@staticmethod
def display_width(data):
"""Gets the width for the data to be display.
Args:
data: The data.
Return:
A number.
"""
return vim.strwidth(VimInfo.transform_to_vim(data))
@staticmethod
def confirm(prompt, options=None, default=None):
"""Confirm something from the user.
Args:
prompt: Prompt string.
options: List of options.
default: Default option.
"""
if options is None:
ret = vim.eval('confirm("%s")' % prompt)
elif default is None:
ret = vim.eval('confirm("%s", "%s")' %
(prompt, '\\n'.join('&' + o for o in options)))
else:
ret = vim.eval('confirm("%s", "%s", "%s")' %
(prompt, '\\n'.join('&' + o for o in options),
default))
return int(ret) - 1
# Copy from https://bitbucket.org/gutworth/six/src/c17477e81e482d34bf3cda043b2eca643084e5fd/six.py
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
class metaclass(meta): # pylint: disable=W0232
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
class VimInfo(with_metaclass(VimInfoMeta, object)): # pylint: disable=W0232
"""An interface for accessing the vim's vars, buffer, cursors, etc."""
pass
########################### About connection to server #########################
class JSONPackageError(Exception):
"""Error raised by JSONPackage."""
pass
class JSONPackage(object):
"""Send/receive json object by gived function.
Attributes:
content: Content of the package body.
Static attributes:
_ENCODING: Encoding of the package.
_HEADER_LENGTH: Length of the header.
"""
_ENCODING = 'utf-8'
_HEADER_LENGTH = 10
def __init__(self, content=None, recv_func=None):
"""Constructor.
If the receive_func is not None, it will grap the default content by
calling that function instead of by the argument "content".
The detail of arguments/return values format see the method "recv_from".
Args:
content: The default content of this package.
recv_func: A function for receive the default content.
"""
self.content = content
if recv_func is not None:
self.recv(recv_func)
def send(self, send_func):
"""Sends by calling the gived sending function.
Args:
send_func: A function which will send the whole data gived.
Function format:
send_func(bytes_data): None
"""
try:
body = json.dumps(self.content)
header_str = ('%%0%dd' % JSONPackage._HEADER_LENGTH) % len(body)
send_func(header_str + body)
except TypeError as e:
raise JSONPackageError('json: %s' % str(e))
except UnicodeError as e:
raise JSONPackageError('Cannot encode the string: %s.' % str(e))
def recv(self, recv_func):
"""Receives a json object from a gived function.
It will calls the give function like this:
recv_func(<num_of_bytes>) => bytes with length <num_of_bytes>
Args:
recv_func: A function to be called to get the serialize data.
"""
try:
header_str = unicode(recv_func(JSONPackage._HEADER_LENGTH),
JSONPackage._ENCODING)
body_str = unicode(recv_func(int(header_str)),
JSONPackage._ENCODING)
except UnicodeError as e:
raise JSONPackageError('Cannot decode the bytes: %r.' % e)
except ValueError as e:
raise JSONPackageError('Cannot get the body length %r' % e)
try:
self.content = json.loads(body_str)
except ValueError as e:
raise JSONPackageError('Cannot loads to the json object: %r' % e)
class TCPConnection(object):
"""My custom tcp connection.
Args:
_conn: The TCP-connection.
"""
def __init__(self, conn):
"""Constructor.
Args:
conn: TCP-connection.
"""
self._conn = conn
self._conn.settimeout(py_bvars.get(VARNAMES.TIMEOUT, DEFAULT_TIMEOUT))
def send_all(self, data):
"""Sends the data until timeout or the socket closed.
Args:
data: Data to be sent.
"""
self._conn.sendall(data)
def recv_all(self, nbyte):
"""Receives the data until timeout or the socket closed.
Args:
nbyte: Bytes of data to receive.
Return:
Bytes of data.
"""
ret = b''
while nbyte > 0:
recv = self._conn.recv(nbyte)
if not recv:
raise socket.error('Connection die.')
ret += recv
nbyte -= len(recv)
return ret
def close(self):
"""Closes the connection."""
self._conn.close()
class TCPClientError(Exception):
"""Exception raised by TCPClient."""
pass
class TCPClient(object):
"""TCP client.
Attributes:
_conn: Connection.
Static attributes:
_conns: A dict stores connections.
"""
_conns = {}
def __init__(self, server_name, port_name):
"""Constructor, automatically connects to the server.
Args:
server_name: Server name.
port_name: Port name.
"""
key = (server_name, port_name)
if key not in TCPClient._conns:
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((server_name, port_name))
TCPClient._conns[key] = TCPConnection(sock)
except TypeError as e:
raise TCPClientError('Cannot connect to server: %s' % str(e))
except socket.error as e:
raise TCPClientError('Cannot connect to server: %s' % str(e))
self._conn = TCPClient._conns[key]
def request(self, req):
"""Sends a request to server and get the response.
Args:
req: An request.
Return:
The response.
"""
try:
JSONPackage(req).send(self._conn.send_all)
return JSONPackage(recv_func=self._conn.recv_all).content
except socket.error as e:
self.close()
raise TCPClientError(e)
except JSONPackageError as e:
raise TCPClientError(e)
def close(self):
"""Closes the socket."""
self._conn.close()
for key, conn in TCPClient._conns.items():
if conn is self._conn:
del TCPClient._conns[key]
break
################################ Some operations ###############################
def init_for_this_time():
py_bvars.curr_scope = vim.current.buffer
py_wvars.curr_scope = vim.current.window
VimInfo.init()
def get_my_info(init):
"""Gets my information for server.
Return:
The information for server.
"""
return {JSON_TOKEN.IDENTITY : py_bvars[VARNAMES.IDENTITY],
JSON_TOKEN.INIT : init,
JSON_TOKEN.MODE : VimInfo.mode,
JSON_TOKEN.CURSORS : {
CURSOR_MARK.CURRENT : VimInfo.cursors[CURSOR_MARK.CURRENT],
CURSOR_MARK.V : VimInfo.cursors[CURSOR_MARK.V],
},
JSON_TOKEN.DIFF : VimInfo.lines.gen_patch(
py_bvars.get(VARNAMES.LINES, ['']))}
def set_my_info(json_info):
"""Sets my information gived by server.
Args:
json_info: JSON information gived by server.
"""
VimInfo.lines.apply_patch(json_info[JSON_TOKEN.DIFF])
py_bvars[VARNAMES.LINES] = VimInfo.lines[:]
mode = json_info[JSON_TOKEN.MODE]
VimInfo.mode = mode
if mode in (MODE.VISUAL, MODE.BLOCK_VISUAL, MODE.LINE_VISUAL):
old_mode, VimInfo.mode = mode, MODE.NORMAL
VimInfo.cursors[CURSOR_MARK.V] = \
json_info[JSON_TOKEN.CURSORS][CURSOR_MARK.V]
VimInfo.mode = old_mode
VimInfo.cursors[CURSOR_MARK.CURRENT] = \
json_info[JSON_TOKEN.CURSORS][CURSOR_MARK.CURRENT]
def set_others_info(json_info):
"""Sets the informations about other user.
Args:
json_info: JSON information gived by server.
"""
users = json_info[JSON_TOKEN.OTHERS]
VimInfo.highlight.reset([user[JSON_TOKEN.NICKNAME] for user in users])
for user in users:
name, mode = user[JSON_TOKEN.NICKNAME], user[JSON_TOKEN.MODE]
cursors = user[JSON_TOKEN.CURSORS]
curr_rc = cursors[CURSOR_MARK.CURRENT]
VimInfo.highlight[name].set_mode_cursor(mode, curr_rc)
if mode in (MODE.VISUAL, MODE.LINE_VISUAL, MODE.BLOCK_VISUAL):
last_rc = cursors[CURSOR_MARK.V]
if last_rc[0] > curr_rc[0] or \
(last_rc[0] == curr_rc[0] and last_rc[1] > curr_rc[1]):
last_rc, curr_rc = curr_rc, last_rc
set_other_visual(name, mode, last_rc, curr_rc)
VimInfo.highlight.render()
def set_other_visual(name, mode, beg, end):
"""Sets the other user's visual block.
Args:
name: Name of this user.
mode: Mode of this user.
beg: The first row-column position of the range.
end: The last row-column position of the range.
"""
if mode == MODE.VISUAL:
for row in range(beg[0], end[0] + 1):
col_beg = 0 if row != beg[0] else beg[1]
col_end = (len(VimInfo.lines[row]) if row != end[0] else end[1]) + 1
VimInfo.highlight[name].add_visual((row, col_beg, col_end))
elif mode == MODE.LINE_VISUAL:
for row in range(beg[0], end[0] + 1):
VimInfo.highlight[name].add_visual(
(row, 0, len(VimInfo.lines[row]) + 1))
elif mode == MODE.BLOCK_VISUAL:
set_other_block_visual(name, beg, end)
def set_other_block_visual(name, beg, end):
"""Sets the other user's virtical-visual block.
Args:
name: Name of this user.
beg: The first row-column position of the range.
end: The last row-column position of the range.
"""
w1 = VimInfo.display_width(VimInfo.lines[beg[0]][ : beg[1]])
w2 = VimInfo.display_width(VimInfo.lines[end[0]][ : end[1]])
left, right = min(w1, w2), max(w1, w2)
for row in range(beg[0], end[0] + 1):
for beg_col in range(len(VimInfo.lines[row]) + 1):
w = VimInfo.display_width(VimInfo.lines[row][ : beg_col])
if left < w:
beg_col -= 1
break
else:
continue
for end_col in range(beg_col, len(VimInfo.lines[row]) + 1):
w = VimInfo.display_width(VimInfo.lines[row][ : end_col])
if right < w:
break
VimInfo.highlight[name].add_visual((row, beg_col, end_col))
############################## Supported operations ############################
def connect(server_name, server_port, identity):
"""Connects to the server.
Args:
server_name: Server name.
server_port: Server port.
identity: Identity string of this user.
"""
init_for_this_time()
if len(VimInfo.lines) > 1 or len(VimInfo.lines[0]) > 0:
c = VimInfo.confirm(
'This tool will rewrite the whole buffer, continue? ',
['Yes', 'No'], 'No')
if c != 0:
return
VimInfo.lines[:] = ['']
py_bvars[VARNAMES.SERVER_NAME] = server_name
py_bvars[VARNAMES.SERVER_PORT] = int(server_port)
py_bvars[VARNAMES.IDENTITY] = identity
sync(init=True)
def sync(init=False):
"""Sync with the server.
Args:
init: Flag for whether it should tell the server to reset this user or
not.
"""
init_for_this_time()
if VARNAMES.SERVER_NAME in py_bvars:
try:
conn = TCPClient(py_bvars[VARNAMES.SERVER_NAME],
py_bvars[VARNAMES.SERVER_PORT])
response = conn.request(get_my_info(init))
except TCPClientError as e:
print(str(e))
return
if JSON_TOKEN.ERROR in response:
print(response[JSON_TOKEN.ERROR])
return
set_my_info(response)
set_others_info(response)
py_bvars[VARNAMES.USERS] = ', '.join(
[user[JSON_TOKEN.NICKNAME] for user in response[JSON_TOKEN.OTHERS]])
def disconnect():
"""Disconnects with the server."""
init_for_this_time()
if VARNAMES.SERVER_NAME in py_bvars:
VimInfo.highlight.reset([])
VimInfo.highlight.render()
try:
conn = TCPClient(py_bvars[VARNAMES.SERVER_NAME],
py_bvars[VARNAMES.SERVER_PORT])
conn.request({
JSON_TOKEN.BYE : True,
JSON_TOKEN.IDENTITY : py_bvars[VARNAMES.IDENTITY]})
except TCPClientError as e:
print(str(e))
conn.close()
del py_bvars[VARNAMES.SERVER_NAME]
del py_bvars[VARNAMES.SERVER_PORT]
del py_bvars[VARNAMES.IDENTITY]
print('bye')
def show_info():
"""Shows the informations."""
init_for_this_time()
print('Highlight information:')
print('Groups of normal cursor position:')
for index in range(VimInfo.highlight.num_of_groups()):
vim.command('hi %s' % NORMAL_CURSOR_GROUP(index))
print('Groups of insert cursor position:')
for index in range(VimInfo.highlight.num_of_groups()):
vim.command('hi %s' % INSERT_CURSOR_GROUP(index))
print('Groups of selection area:')
for index in range(VimInfo.highlight.num_of_groups()):
vim.command('hi %s' % VISUAL_GROUP(index))
print('Users: %r' % py_bvars[VARNAMES.USERS])
def set_bvar(variable_name, value):
"""Sets the variable of the current buffer.
Args:
variable_name: Variable name.
value: Value.
"""
init_for_this_time()
py_bvars[getattr(VARNAMES, variable_name)] = value
print('bvars[%s] = %s' % (getattr(VARNAMES, variable_name), value))
EOF
endfunction
"""""""""""""""""""""""""""""""" Initialize """"""""""""""""""""""""""""""""""""
if _ShrVimTryUsePython3(0) || _ShrVimTryUsePython2(0)
let g:shrvim_setupped = 1
endif