diff --git a/README.markdown b/README.markdown index 27c10c0..f536099 100644 --- a/README.markdown +++ b/README.markdown @@ -8,6 +8,9 @@ * [FAQ](#faq) # Changes +* 28th of December, 2016 + * Implemented Clang-based source code [syntax highlighting](docs/services_framework.md#syntax-highlighting) service + (run `cd /colors/yaflandia && git pull` to get required colorscheme changes) * 1st of July, 2016 * Implemented new generic client-server (async) [framework](docs/services_framework.md#framework) which enables dispatching any kind of operations to run in a separate non-blocking background processes (so called [services](docs/services_framework.md#services)) and upon whose completion results can be reported back to the server ('Yavide'). @@ -44,7 +47,7 @@ See [some GIFs in action](docs/services_framework.md). * Backed by real C/C++ compiler back-end to ensure total correctness * Source code navigation * Featuring a fully automated tag generation system which keeps the symbol database up-to-date -* Source code syntax highlighting +* Source code syntax highlighting based on `libclang` * Providing more rich syntax highlighting support than the one provided originally by `Vim` * Source code auto-formatting * `clang-formatter` support diff --git a/core/.api.vimrc b/core/.api.vimrc index 5876179..06f255b 100644 --- a/core/.api.vimrc +++ b/core/.api.vimrc @@ -1,8 +1,22 @@ +" -------------------------------------------------------------------------------------------------------------------------------------- +" +" SCRIPT LOCAL VARIABLES +" +" -------------------------------------------------------------------------------------------------------------------------------------- +let s:y_prev_line = 0 +let s:y_prev_col = 0 +let s:y_prev_char = '' + " -------------------------------------------------------------------------------------------------------------------------------------- " " YAVIDE VIMSCRIPT UTILS " " -------------------------------------------------------------------------------------------------------------------------------------- +" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Function: Y_Utils_AppendToFile() +" Description: Writes 'lines' to 'file' +" Dependency: +" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" function! s:Y_Utils_AppendToFile(file, lines) call writefile(readfile(a:file) + a:lines, a:file) endfunction @@ -132,6 +146,7 @@ function! s:Y_Project_Create(bEmptyProject) call add(l:project_settings, 'let g:' . 'project_name = ' . "\'" . l:project_name . "\'") call add(l:project_settings, 'let g:' . 'project_category = ' . l:project_category) call add(l:project_settings, 'let g:' . 'project_type = ' . l:project_type) + call add(l:project_settings, 'let g:' . 'project_compiler_args = ' . "\'\'") call writefile(l:project_settings, g:project_configuration_filename) return 0 endif @@ -833,10 +848,7 @@ endfunction " Dependency: " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" function! Y_SrcCodeHighlighter_Start() -"python import sys -"python import vim -"python sys.argv = ['', vim.eval('l:currentBuffer'), "/tmp", "-n", "-c", "-s", "-e", "-ev", "-u", "-cusm", "-lv", "-vd", "-fp", "-fd", "-t", "-m", "-efwd"] - call Y_ServerStartService(g:project_service_src_code_highlighter['id'], 'some param') + call Y_ServerStartService(g:project_service_src_code_highlighter['id'], 'dummy_param') endfunction " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -848,14 +860,65 @@ function! Y_SrcCodeHighlighter_Stop() call Y_ServerStopService(g:project_service_src_code_highlighter['id']) endfunction +" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Function: Y_SrcCodeHighlighter_Reset() +" Description: Resets variables to initial state. +" Dependency: +" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +function! Y_SrcCodeHighlighter_Reset() + let s:y_prev_line = 0 + let s:y_prev_col = 0 + let s:y_prev_char = '' +endfunction + " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " Function: Y_SrcCodeHighlighter_Run() " Description: Triggers the source code highlighting for current buffer. " Dependency: " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" function! Y_SrcCodeHighlighter_Run() - let l:currentBuffer = expand('%:p') - call Y_ServerSendMsg(g:project_service_src_code_highlighter['id'], l:currentBuffer) + let l:current_buffer = expand('%:p') + let l:contents_filename = l:current_buffer + let l:compiler_args = g:project_compiler_args + + " If buffer contents are modified but not saved, we need to serialize contents of the current buffer into temporary file. + let l:bufferModified = getbufvar(bufnr('%'), '&modified') + if l:bufferModified == 1 + let l:contents_filename = '/tmp/yavideTempBufferContents' + +python << EOF +import vim +import os + +# Serialize the contents +temp_file = open(vim.eval('l:contents_filename'), "w", 0) +temp_file.writelines(line + '\n' for line in vim.current.buffer) + +# Append additional include path to the compiler args which points to the parent directory of current buffer. +# * This needs to be done because we will be doing analysis on tmp file which is outside the project directory. +# By doing this, we might invalidate header includes for that particular file and therefore trigger unnecessary +# Clang parsing errors. +# * An alternative would be to generate tmp files in original location but that would pollute project directory and +# potentially would not play well with other tools (indexer, version control, etc.). +vim.command("let l:compiler_args .= '" + " -I" + os.path.dirname(vim.eval("l:current_buffer")) + "'") +EOF + + endif + + call Y_ServerSendMsg(g:project_service_src_code_highlighter['id'], [l:contents_filename, l:current_buffer, l:compiler_args, g:project_root_directory]) +endfunction + +" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Function: Y_SrcCodeHighlighter_RunConditionally() +" Description: Conditionally runs the source code highlighter for current buffer. +" Tries to minimize triggering the syntax highlighter in some certain cases +" when it is not absolutely unnecessary (i.e. when typing letter after letter). +" Dependency: +" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +function! Y_SrcCodeHighlighter_RunConditionally() + if Y_SrcCodeHighlighter_CheckTextChangedType() + call Y_SrcCodeHighlighter_Run() + endif endfunction " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -864,11 +927,8 @@ endfunction " Dependency: " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" function! Y_SrcCodeHighlighter_Apply(filename, syntax_file) - let l:currentBuffer = expand('%:p') - if l:currentBuffer == a:filename - " Clear previously generated syntax rules - execute('syntax clear yavideCppNamespace yavideCppClass yavideCppStructure yavideCppEnum yavideCppEnumValue yavideCppUnion yavideCppClassStructUnionMember yavideCppLocalVariable yavideCppVariableDefinition yavideCppFunctionPrototype yavideCppFunctionDefinition yavideCppMacro yavideCppTypedef yavideCppExternForwardDeclaration') - + let l:current_buffer = expand('%:p') + if l:current_buffer == a:filename " Apply the syntax highlighting rules execute('source '.a:syntax_file) @@ -879,6 +939,57 @@ function! Y_SrcCodeHighlighter_Apply(filename, syntax_file) endif endfunction +" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Function: Y_SrcCodeHighlighter_CheckTextChangedType() +" Description: Implements simple heuristics to detect what kind of text change has taken place in current buffer. +" This is useful if one wants to install handler for 'TextChanged' events but not necessarily +" act on each of those because they are triggered rather frequently. This is by no means a perfect +" implementation but it tries to give good enough approximations. It probably can be improved and specialized further. +" Returns 0 for a non-interesting change. Otherwise, some value != 0. +" Dependency: +" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +function! Y_SrcCodeHighlighter_CheckTextChangedType() + + let l:textChangeType = 0 " no interesting change (i.e. typed in a letter after letter) + +python << EOF +import vim + +# Uncomment to enable debugging +#import logging +#logging.basicConfig(filename='/tmp/temp', filemode='w', level=logging.INFO) +#logging.info("y_prev_line = '{0}' y_prev_col = '{1}' y_prev_char = '{2}'. curr_line = '{3}' curr_col = '{4}' curr_char = '{5}'".format(vim.eval('s:y_prev_line'), vim.eval('s:y_prev_col'), vim.eval('s:y_prev_char'), curr_line, curr_col, curr_char)) + +curr_line = int(vim.eval("line('.')")) +curr_col = int(vim.eval("col('.')")) +curr_char = str(vim.eval("getline('.')[col('.')-2]")) + +if curr_line > int(vim.eval('s:y_prev_line')): + vim.command("let l:textChangeType = 1") #logging.info("Switched to next line!") +elif curr_line < int(vim.eval('s:y_prev_line')): + vim.command("let l:textChangeType = 2") #logging.info("Switched to previous line!") +else: + if not curr_char.isalnum(): + if str(vim.eval('s:y_prev_char')).isalnum(): + vim.command("let l:textChangeType = 3") #logging.info("Delimiter!") + else: + if curr_col > int(vim.eval('s:y_prev_col')): #logging.info("---> '{0}'".format(vim.eval("getline('.')")[curr_col-1:])) + if len(vim.eval("getline('.')")[curr_col-1:]) > 0: + vim.command("let l:textChangeType = 3") + elif curr_col < int(vim.eval('s:y_prev_col')): #logging.info("<--- '{0}'".format(vim.eval("getline('.')")[:curr_col-1])) + if len(vim.eval("getline('.')")[curr_col-1:]) > 0: + vim.command("let l:textChangeType = 3") + +vim.command('let s:y_prev_line = %s' % curr_line) +vim.command('let s:y_prev_col = %s' % curr_col) +vim.command('let s:y_prev_char = "%s"' % curr_char.replace('"', "\"").replace("\\", "\\\\")) + +EOF + + return l:textChangeType + +endfunction + " -------------------------------------------------------------------------------------------------------------------------------------- " " STATIC ANALYSIS API @@ -1003,8 +1114,8 @@ endfunction " Dependency: " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" function! Y_SrcCodeFormatter_Run() - let l:currentBuffer = expand('%:p') - call Y_ServerSendMsg(g:project_service_src_code_formatter['id'], l:currentBuffer) + let l:current_buffer = expand('%:p') + call Y_ServerSendMsg(g:project_service_src_code_formatter['id'], l:current_buffer) endfunction " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -1013,8 +1124,8 @@ endfunction " Dependency: " """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" function! Y_SrcCodeFormatter_Apply(filename) - let l:currentBuffer = expand('%:p') - if l:currentBuffer == a:filename + let l:current_buffer = expand('%:p') + if l:current_buffer == a:filename execute('e') endif endfunction diff --git a/core/.autocommands.vimrc b/core/.autocommands.vimrc index c3c7ceb..552c7a7 100644 --- a/core/.autocommands.vimrc +++ b/core/.autocommands.vimrc @@ -20,8 +20,12 @@ augroup END augroup yavide_src_code_highlight_group autocmd! + autocmd BufEnter * if index(['c', 'cpp'], &ft) < 0 | call clearmatches() | endif " We need to clear matches when entering non-Cxx buffers + autocmd BufEnter *.cpp,*.cc,*.c,*.h,*.hh,*.hpp call Y_SrcCodeHighlighter_Reset() autocmd BufEnter *.cpp,*.cc,*.c,*.h,*.hh,*.hpp call Y_SrcCodeHighlighter_Run() autocmd BufWritePost *.cpp,*.cc,*.c,*.h,*.hh,*.hpp call Y_SrcCodeHighlighter_Run() + autocmd TextChanged *.cpp,*.cc,*.c,*.h,*.hh,*.hpp call Y_SrcCodeHighlighter_Run() + autocmd TextChangedI *.cpp,*.cc,*.c,*.h,*.hh,*.hpp call Y_SrcCodeHighlighter_RunConditionally() augroup END augroup yavide_layout_mgmt_group diff --git a/core/.globals.vimrc b/core/.globals.vimrc index 12edda3..f52a086 100644 --- a/core/.globals.vimrc +++ b/core/.globals.vimrc @@ -12,6 +12,7 @@ let g:project_java_tags_filename = '.java_tags' let g:project_cxx_tags = '' let g:project_cxx_tags_filename = '.cxx_tags' let g:project_cscope_db_filename = 'cscope.out' +let g:project_compiler_args = '' let g:project_env_build_preproces_command = '' let g:project_env_build_command = '' let g:project_env_src_code_format_config = '.clang-format' diff --git a/core/services/syntax_highlighter/clang_tokenizer.py b/core/services/syntax_highlighter/clang_tokenizer.py new file mode 100644 index 0000000..39f8239 --- /dev/null +++ b/core/services/syntax_highlighter/clang_tokenizer.py @@ -0,0 +1,118 @@ +import sys +import logging +import subprocess +import clang.cindex +from services.syntax_highlighter.token_identifier import TokenIdentifier + +def get_system_includes(): + output = subprocess.Popen(["g++", "-v", "-E", "-x", "c++", "-"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + pattern = ["#include <...> search starts here:", "End of search list."] + output = str(output) + return output[output.find(pattern[0]) + len(pattern[0]) : output.find(pattern[1])].replace(' ', '-I').split('\\n') + +class ClangTokenizer(): + def __init__(self): + self.filename = '' + self.token_list = [] + self.index = clang.cindex.Index.create() + self.default_args = ['-x', 'c++', '-std=c++14'] + get_system_includes() + + def run(self, filename, compiler_args, project_root_directory): + self.filename = filename + self.token_list = [] + logging.info('Filename = {0}'.format(self.filename)) + logging.info('Default args = {0}'.format(self.default_args)) + logging.info('User-provided compiler args = {0}'.format(compiler_args)) + logging.info('Compiler working-directory = {0}'.format(project_root_directory)) + translation_unit = self.index.parse( + path = self.filename, + args = self.default_args + compiler_args + ['-working-directory=' + project_root_directory], + options = clang.cindex.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD + ) + + diag = translation_unit.diagnostics + for d in diag: + logging.info('Parsing error: ' + str(d)) + + logging.info('Translation unit: '.format(translation_unit.spelling)) + self.__visit_all_nodes(translation_unit.cursor) + + def get_token_list(self): + return self.token_list + + def get_token_id(self, token): + if token.referenced: + return ClangTokenizer.to_token_id(token.referenced.kind) + return ClangTokenizer.to_token_id(token.kind) + + def get_token_name(self, token): + if (token.referenced): + return token.referenced.spelling + else: + return token.spelling + + def get_token_line(self, token): + return token.location.line + + def get_token_column(self, token): + return token.location.column + + def dump_token_list(self): + for idx, token in enumerate(self.token_list): + logging.debug( + '%-12s' % ('[' + str(token.location.line) + ', ' + str(token.location.column) + ']') + + '%-40s ' % str(token.spelling) + + '%-40s ' % str(token.kind) + + ('%-40s ' % str(token.referenced.spelling) if (token.kind.is_reference()) else '') + + ('%-40s ' % str(token.referenced.kind) if (token.kind.is_reference()) else '')) + + def __visit_all_nodes(self, node): + for n in node.get_children(): + if n.location.file and n.location.file.name == self.filename: + self.token_list.append(n) + self.__visit_all_nodes(n) + + @staticmethod + def to_token_id(kind): + if (kind == clang.cindex.CursorKind.NAMESPACE): + return TokenIdentifier.getNamespaceId() + if (kind in [clang.cindex.CursorKind.CLASS_DECL, clang.cindex.CursorKind.CLASS_TEMPLATE, clang.cindex.CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION]): + return TokenIdentifier.getClassId() + if (kind == clang.cindex.CursorKind.STRUCT_DECL): + return TokenIdentifier.getStructId() + if (kind == clang.cindex.CursorKind.ENUM_DECL): + return TokenIdentifier.getEnumId() + if (kind == clang.cindex.CursorKind.ENUM_CONSTANT_DECL): + return TokenIdentifier.getEnumValueId() + if (kind == clang.cindex.CursorKind.UNION_DECL): + return TokenIdentifier.getUnionId() + if (kind == clang.cindex.CursorKind.FIELD_DECL): + return TokenIdentifier.getFieldId() + if (kind == clang.cindex.CursorKind.VAR_DECL): + return TokenIdentifier.getLocalVariableId() + if (kind in [clang.cindex.CursorKind.FUNCTION_DECL, clang.cindex.CursorKind.FUNCTION_TEMPLATE]): + return TokenIdentifier.getFunctionId() + if (kind in [clang.cindex.CursorKind.CXX_METHOD, clang.cindex.CursorKind.CONSTRUCTOR, clang.cindex.CursorKind.DESTRUCTOR]): + return TokenIdentifier.getMethodId() + if (kind == clang.cindex.CursorKind.PARM_DECL): + return TokenIdentifier.getFunctionParameterId() + if (kind == clang.cindex.CursorKind.TEMPLATE_TYPE_PARAMETER): + return TokenIdentifier.getTemplateTypeParameterId() + if (kind == clang.cindex.CursorKind.TEMPLATE_NON_TYPE_PARAMETER): + return TokenIdentifier.getTemplateNonTypeParameterId() + if (kind == clang.cindex.CursorKind.TEMPLATE_TEMPLATE_PARAMETER): + return TokenIdentifier.getTemplateTemplateParameterId() + if (kind == clang.cindex.CursorKind.MACRO_DEFINITION): + return TokenIdentifier.getMacroDefinitionId() + if (kind == clang.cindex.CursorKind.MACRO_INSTANTIATION): + return TokenIdentifier.getMacroInstantiationId() + if (kind in [clang.cindex.CursorKind.TYPEDEF_DECL, clang.cindex.CursorKind.TYPE_ALIAS_DECL]): + return TokenIdentifier.getTypedefId() + if (kind == clang.cindex.CursorKind.NAMESPACE_ALIAS): + return TokenIdentifier.getNamespaceAliasId() + if (kind == clang.cindex.CursorKind.USING_DIRECTIVE): + return TokenIdentifier.getUsingDirectiveId() + if (kind == clang.cindex.CursorKind.USING_DECLARATION): + return TokenIdentifier.getUsingDeclarationId() + return TokenIdentifier.getUnsupportedId() + diff --git a/core/services/syntax_highlighter/ctags_tokenizer.py b/core/services/syntax_highlighter/ctags_tokenizer.py new file mode 100644 index 0000000..204e6eb --- /dev/null +++ b/core/services/syntax_highlighter/ctags_tokenizer.py @@ -0,0 +1,76 @@ +import sys +import shlex +import logging +import os.path +from subprocess import call +from services.syntax_highlighter.token_identifier import TokenIdentifier + +class CtagsTokenizer(): + def __init__(self, tag_db_path): + self.tag_db_path = tag_db_path + + def run(self, path): + self.__generate_ctags_db(path) + + def is_header(self, tag_line): + if tag_line.startswith("!_TAG_"): # ctags tag file header + return True + else: + return False + + def get_token_id(self, tag_line): + s = tag_line.split() + if s: + return CtagsTokenizer.to_token_id(s[len(s)-1]) + else: + return "" + + def get_token_name(self, tag_line): + s = tag_line.split() + if s: + return s[0] + else: + return "" + + def __generate_ctags_db(self, path): + if os.path.exists(path): + cmd = 'ctags --languages=C,C++ --fields=K --extra=-fq ' + '--c++-kinds=ncsgeumlvpfdtx' + ' -f ' + self.tag_db_path + ' ' + if os.path.isdir(path): + cmd += '-R ' + cmd += path + logging.info("Generating the db: '{0}'".format(cmd)) + call(shlex.split(cmd)) + else: + logging.error("Non-existing path '{0}'.".format(path)) + + @staticmethod + def to_token_id(kind): + if (kind == "namespace"): + return TokenIdentifier.getNamespaceId() + if (kind == "class"): + return TokenIdentifier.getClassId() + if (kind == "struct"): + return TokenIdentifier.getStructId() + if (kind == "enum"): + return TokenIdentifier.getEnumId() + if (kind == "enumerator"): + return TokenIdentifier.getEnumValueId() + if (kind == "union"): + return TokenIdentifier.getUnionId() + if (kind == "member"): + return TokenIdentifier.getClassStructUnionMemberId() + if (kind == "local"): + return TokenIdentifier.getLocalVariableId() + if (kind == "variable"): + return TokenIdentifier.getVariableDefinitionId() + if (kind == "prototype"): + return TokenIdentifier.getFunctionPrototypeId() + if (kind == "function"): + return TokenIdentifier.getFunctionDefinitionId() + if (kind == "macro"): + return TokenIdentifier.getMacroId() + if (kind == "typedef"): + return TokenIdentifier.getTypedefId() + if (kind == "externvar"): + return TokenIdentifier.getExternFwdDeclarationId() + diff --git a/core/services/syntax_highlighter/syntax_highlighter.py b/core/services/syntax_highlighter/syntax_highlighter.py index 9f01c98..484e077 100644 --- a/core/services/syntax_highlighter/syntax_highlighter.py +++ b/core/services/syntax_highlighter/syntax_highlighter.py @@ -1,29 +1,66 @@ import sys import argparse import logging -from services.syntax_highlighter.tag_identifier import TagIdentifier -from services.syntax_highlighter.tag_generator import TagGenerator +from services.syntax_highlighter.token_identifier import TokenIdentifier +from services.syntax_highlighter.ctags_tokenizer import CtagsTokenizer +from services.syntax_highlighter.clang_tokenizer import ClangTokenizer + +# TODO remove this once 'Unsupported token id' message is removed +import clang.cindex class VimSyntaxHighlighter: - def __init__(self, tag_id_list, output_syntax_file): - self.tag_id_list = tag_id_list + def __init__(self, output_syntax_file): self.output_syntax_file = output_syntax_file - self.output_tag_file = "/tmp/yavide_tags" - def generate_vim_syntax_file(self, filename): + def generate_vim_syntax_file_from_clang(self, filename, compiler_args, project_root_directory): + # Generate the tokens + tokenizer = ClangTokenizer() + tokenizer.run(filename, compiler_args, project_root_directory) + + # Build Vim syntax highlight rules + vim_syntax_element = ['call clearmatches()\n'] + token_list = tokenizer.get_token_list() + for token in token_list: + token_id = tokenizer.get_token_id(token) + if token_id != TokenIdentifier.getUnsupportedId(): + highlight_rule = self.__tag_id_to_vim_syntax_group(token_id) + " " + tokenizer.get_token_name(token) + vim_syntax_element.append( + "call matchaddpos('" + + str(self.__tag_id_to_vim_syntax_group(token_id)) + + "', [[" + + str(tokenizer.get_token_line(token)) + + ", " + + str(tokenizer.get_token_column(token)) + + ", " + + str(len(tokenizer.get_token_name(token))) + + "]], -1)" + + "\n" + ) + else: + logging.debug("Unsupported token id: [{0}, {1}]: {2} '{3}'".format(token.location.line, token.location.column, token.kind, tokenizer.get_token_name(token))) + + # Write Vim syntax file + vim_syntax_file = open(self.output_syntax_file, "w") + vim_syntax_file.writelines(vim_syntax_element) + + # Write some debug information + tokenizer.dump_token_list() + + def generate_vim_syntax_file_from_ctags(self, filename): # Generate the tags - tag_generator = TagGenerator(self.tag_id_list, self.output_tag_file) - tag_generator.run(filename) + output_tag_file = "/tmp/yavide_tags" + tokenizer = CtagsTokenizer(output_tag_file) + tokenizer.run(filename) # Generate the vim syntax file tags_db = None try: - tags_db = open(self.output_tag_file) + tags_db = open(output_tag_file) # Build Vim syntax highlight rules vim_highlight_rules = set() for line in tags_db: - if not tag_generator.is_header(line): - highlight_rule = self.__tag_id_to_vim_syntax_group(tag_generator.get_tag_id(line)) + " " + tag_generator.get_tag_name(line) + if not tokenizer.is_header(line): + highlight_rule = self.__tag_id_to_vim_syntax_group(tokenizer.get_token_id(line)) + " " + tokenizer.get_token_name(line) vim_highlight_rules.add(highlight_rule) vim_syntax_element = [] @@ -38,63 +75,56 @@ def generate_vim_syntax_file(self, filename): tags_db.close() def __tag_id_to_vim_syntax_group(self, tag_identifier): - if tag_identifier == TagIdentifier.getNamespaceId(): + if tag_identifier == TokenIdentifier.getNamespaceId(): return "yavideCppNamespace" - if tag_identifier == TagIdentifier.getClassId(): + if tag_identifier == TokenIdentifier.getNamespaceAliasId(): + return "yavideCppNamespaceAlias" + if tag_identifier == TokenIdentifier.getClassId(): return "yavideCppClass" - if tag_identifier == TagIdentifier.getStructId(): + if tag_identifier == TokenIdentifier.getStructId(): return "yavideCppStructure" - if tag_identifier == TagIdentifier.getEnumId(): + if tag_identifier == TokenIdentifier.getEnumId(): return "yavideCppEnum" - if tag_identifier == TagIdentifier.getEnumValueId(): + if tag_identifier == TokenIdentifier.getEnumValueId(): return "yavideCppEnumValue" - if tag_identifier == TagIdentifier.getUnionId(): + if tag_identifier == TokenIdentifier.getUnionId(): return "yavideCppUnion" - if tag_identifier == TagIdentifier.getClassStructUnionMemberId(): - return "yavideCppClassStructUnionMember" - if tag_identifier == TagIdentifier.getLocalVariableId(): + if tag_identifier == TokenIdentifier.getFieldId(): + return "yavideCppField" + if tag_identifier == TokenIdentifier.getLocalVariableId(): return "yavideCppLocalVariable" - if tag_identifier == TagIdentifier.getVariableDefinitionId(): - return "yavideCppVariableDefinition" - if tag_identifier == TagIdentifier.getFunctionPrototypeId(): - return "yavideCppFunctionPrototype" - if tag_identifier == TagIdentifier.getFunctionDefinitionId(): - return "yavideCppFunctionDefinition" - if tag_identifier == TagIdentifier.getMacroId(): - return "yavideCppMacro" - if tag_identifier == TagIdentifier.getTypedefId(): + if tag_identifier == TokenIdentifier.getFunctionId(): + return "yavideCppFunction" + if tag_identifier == TokenIdentifier.getMethodId(): + return "yavideCppMethod" + if tag_identifier == TokenIdentifier.getFunctionParameterId(): + return "yavideCppFunctionParameter" + if tag_identifier == TokenIdentifier.getTemplateTypeParameterId(): + return "yavideCppTemplateTypeParameter" + if tag_identifier == TokenIdentifier.getTemplateNonTypeParameterId(): + return "yavideCppTemplateNonTypeParameter" + if tag_identifier == TokenIdentifier.getTemplateTemplateParameterId(): + return "yavideCppTemplateTemplateParameter" + if tag_identifier == TokenIdentifier.getMacroDefinitionId(): + return "yavideCppMacroDefinition" + if tag_identifier == TokenIdentifier.getMacroInstantiationId(): + return "yavideCppMacroInstantiation" + if tag_identifier == TokenIdentifier.getTypedefId(): return "yavideCppTypedef" - if tag_identifier == TagIdentifier.getExternFwdDeclarationId(): - return "yavideCppExternForwardDeclaration" + if tag_identifier == TokenIdentifier.getUsingDirectiveId(): + return "yavideCppUsingDirective" + if tag_identifier == TokenIdentifier.getUsingDeclarationId(): + return "yavideCppUsingDeclaration" def main(): parser = argparse.ArgumentParser() - parser.add_argument("-n", "--" + TagIdentifier.getNamespaceId(), help="enable namespace highlighting", action="store_true") - parser.add_argument("-c", "--" + TagIdentifier.getClassId(), help="enable class highlighting", action="store_true") - parser.add_argument("-s", "--" + TagIdentifier.getStructId(), help="enable struct highlighting", action="store_true") - parser.add_argument("-e", "--" + TagIdentifier.getEnumId(), help="enable enum highlighting", action="store_true") - parser.add_argument("-ev", "--" + TagIdentifier.getEnumValueId(), help="enable enum values highlighting", action="store_true") - parser.add_argument("-u", "--" + TagIdentifier.getUnionId(), help="enable union highlighting", action="store_true") - parser.add_argument("-cusm", "--" + TagIdentifier.getClassStructUnionMemberId(), help="enable class/union/struct member highlighting", action="store_true") - parser.add_argument("-lv", "--" + TagIdentifier.getLocalVariableId(), help="enable local variable highlighting", action="store_true") - parser.add_argument("-vd", "--" + TagIdentifier.getVariableDefinitionId(), help="enable variable definition highlighting", action="store_true") - parser.add_argument("-fp", "--" + TagIdentifier.getFunctionPrototypeId(), help="enable function declaration highlighting", action="store_true") - parser.add_argument("-fd", "--" + TagIdentifier.getFunctionDefinitionId(), help="enable function definition highlighting", action="store_true") - parser.add_argument("-t", "--" + TagIdentifier.getTypedefId(), help="enable typedef highlighting", action="store_true") - parser.add_argument("-m", "--" + TagIdentifier.getMacroId(), help="enable macro highlighting", action="store_true") - parser.add_argument("-efwd", "--" + TagIdentifier.getExternFwdDeclarationId(), help="enable extern & forward declaration highlighting", action="store_true") parser.add_argument("filename", help="source code file to generate the source code highlighting for") parser.add_argument("output_syntax_file", help="resulting Vim syntax file") args = parser.parse_args() args_dict = vars(args) - tag_id_list = list() - for key, value in args_dict.iteritems(): - if value == True: - tag_id_list.append(key) - - vimHighlighter = VimSyntaxHighlighter(tag_id_list, args.output_syntax_file) - vimHighlighter.generate_vim_syntax_file(args.filename) + vimHighlighter = VimSyntaxHighlighter(args.output_syntax_file) + vimHighlighter.generate_vim_syntax_file_from_clang(args.filename, ['']) if __name__ == "__main__": main() diff --git a/core/services/syntax_highlighter/tag_generator.py b/core/services/syntax_highlighter/tag_generator.py deleted file mode 100644 index f5cd037..0000000 --- a/core/services/syntax_highlighter/tag_generator.py +++ /dev/null @@ -1,115 +0,0 @@ -import sys -import shlex -import logging -import os.path -from subprocess import call -from services.syntax_highlighter.tag_identifier import TagIdentifier - -class TagGenerator(): - def __init__(self, tag_id_list, tag_db_path): - self.tag_id_list = tag_id_list - self.tag_db_path = tag_db_path - - def run(self, path): - self.__generate_ctags_db(path) - - def is_header(self, tag_line): - if tag_line.startswith("!_TAG_"): # ctags tag file header - return True - else: - return False - - def get_tag_id(self, tag_line): - s = tag_line.split() - if s: - return CtagsTagGenerator.to_tag_id(s[len(s)-1]) - else: - return "" - - def get_tag_name(self, tag_line): - s = tag_line.split() - if s: - return s[0] - else: - return "" - - def __generate_ctags_db(self, path): - if os.path.exists(path): - cmd = 'ctags --languages=C,C++ --fields=K --extra=-fq ' + '--c++-kinds=' + self.__tag_id_list_to_ctags_kinds_list() + ' -f ' + self.tag_db_path + ' ' - if os.path.isdir(path): - cmd += '-R ' - cmd += path - logging.info("Generating the db: '{0}'".format(cmd)) - call(shlex.split(cmd)) - else: - logging.error("Non-existing path '{0}'.".format(path)) - - def __tag_id_list_to_ctags_kinds_list(self): - ctags_kind_list = '' - for tag_id in self.tag_id_list: - ctags_kind_list += CtagsTagGenerator.from_tag_id(tag_id) - return ctags_kind_list - -class CtagsTagGenerator(): - @staticmethod - def to_tag_id(kind): - if (kind == "namespace"): - return TagIdentifier.getNamespaceId() - if (kind == "class"): - return TagIdentifier.getClassId() - if (kind == "struct"): - return TagIdentifier.getStructId() - if (kind == "enum"): - return TagIdentifier.getEnumId() - if (kind == "enumerator"): - return TagIdentifier.getEnumValueId() - if (kind == "union"): - return TagIdentifier.getUnionId() - if (kind == "member"): - return TagIdentifier.getClassStructUnionMemberId() - if (kind == "local"): - return TagIdentifier.getLocalVariableId() - if (kind == "variable"): - return TagIdentifier.getVariableDefinitionId() - if (kind == "prototype"): - return TagIdentifier.getFunctionPrototypeId() - if (kind == "function"): - return TagIdentifier.getFunctionDefinitionId() - if (kind == "macro"): - return TagIdentifier.getMacroId() - if (kind == "typedef"): - return TagIdentifier.getTypedefId() - if (kind == "externvar"): - return TagIdentifier.getExternFwdDeclarationId() - - @staticmethod - def from_tag_id(tag_id): - if tag_id == TagIdentifier.getNamespaceId(): - return "n" - if tag_id == TagIdentifier.getClassId(): - return "c" - if tag_id == TagIdentifier.getStructId(): - return "s" - if tag_id == TagIdentifier.getEnumId(): - return "g" - if tag_id == TagIdentifier.getEnumValueId(): - return "e" - if tag_id == TagIdentifier.getUnionId(): - return "u" - if tag_id == TagIdentifier.getClassStructUnionMemberId(): - return "m" - if tag_id == TagIdentifier.getLocalVariableId(): - return "l" - if tag_id == TagIdentifier.getVariableDefinitionId(): - return "v" - if tag_id == TagIdentifier.getFunctionPrototypeId(): - return "p" - if tag_id == TagIdentifier.getFunctionDefinitionId(): - return "f" - if tag_id == TagIdentifier.getMacroId(): - return "d" - if tag_id == TagIdentifier.getTypedefId(): - return "t" - if tag_id == TagIdentifier.getExternFwdDeclarationId(): - return "x" - diff --git a/core/services/syntax_highlighter/tag_identifier.py b/core/services/syntax_highlighter/tag_identifier.py deleted file mode 100644 index 4ce7753..0000000 --- a/core/services/syntax_highlighter/tag_identifier.py +++ /dev/null @@ -1,57 +0,0 @@ -class TagIdentifier(): - @staticmethod - def getNamespaceId(): - return "namespace" - - @staticmethod - def getClassId(): - return "class" - - @staticmethod - def getStructId(): - return "struct" - - @staticmethod - def getEnumId(): - return "enum" - - @staticmethod - def getEnumValueId(): - return "enum_value" - - @staticmethod - def getUnionId(): - return "union" - - @staticmethod - def getClassStructUnionMemberId(): - return "class_struct_union_member" - - @staticmethod - def getLocalVariableId(): - return "local_variable" - - @staticmethod - def getVariableDefinitionId(): - return "variable_definition" - - @staticmethod - def getFunctionPrototypeId(): - return "function_prototype" - - @staticmethod - def getFunctionDefinitionId(): - return "function_definition" - - @staticmethod - def getMacroId(): - return "macro" - - @staticmethod - def getTypedefId(): - return "typedef" - - @staticmethod - def getExternFwdDeclarationId(): - return "extern_fwd_declaration" - diff --git a/core/services/syntax_highlighter/token_identifier.py b/core/services/syntax_highlighter/token_identifier.py new file mode 100644 index 0000000..4b51fcd --- /dev/null +++ b/core/services/syntax_highlighter/token_identifier.py @@ -0,0 +1,85 @@ +class TokenIdentifier(): + @staticmethod + def getNamespaceId(): + return "namespace" + + @staticmethod + def getNamespaceAliasId(): + return "namespace_alias" + + @staticmethod + def getClassId(): + return "class" + + @staticmethod + def getStructId(): + return "struct" + + @staticmethod + def getEnumId(): + return "enum" + + @staticmethod + def getEnumValueId(): + return "enum_value" + + @staticmethod + def getUnionId(): + return "union" + + @staticmethod + def getFieldId(): + return "class_struct_union_field" + + @staticmethod + def getLocalVariableId(): + return "local_variable" + + @staticmethod + def getFunctionId(): + return "function" + + @staticmethod + def getMethodId(): + return "method" + + @staticmethod + def getFunctionParameterId(): + return "function_or_method_parameter" + + @staticmethod + def getTemplateTypeParameterId(): + return "template_type_parameter" + + @staticmethod + def getTemplateNonTypeParameterId(): + return "template_non_type_parameter" + + @staticmethod + def getTemplateTemplateParameterId(): + return "template_template_parameter" + + @staticmethod + def getMacroDefinitionId(): + return "macro_definition" + + @staticmethod + def getMacroInstantiationId(): + return "macro_instantiation" + + @staticmethod + def getTypedefId(): + return "typedef" + + @staticmethod + def getUsingDirectiveId(): + return "using_directive" + + @staticmethod + def getUsingDeclarationId(): + return "using_declaration" + + @staticmethod + def getUnsupportedId(): + return "unsupported" + diff --git a/core/services/syntax_highlighter_service.py b/core/services/syntax_highlighter_service.py index da45558..e31ebc2 100644 --- a/core/services/syntax_highlighter_service.py +++ b/core/services/syntax_highlighter_service.py @@ -2,36 +2,21 @@ import time from yavide_service import YavideService from services.syntax_highlighter.syntax_highlighter import VimSyntaxHighlighter -from services.syntax_highlighter.tag_identifier import TagIdentifier from common.yavide_utils import YavideUtils class SyntaxHighlighter(YavideService): def __init__(self, server_queue, yavide_instance): YavideService.__init__(self, server_queue, yavide_instance) self.output_syntax_file = "/tmp/yavideSyntaxFile.vim" - self.tag_id_list = [ - TagIdentifier.getClassId(), - TagIdentifier.getClassStructUnionMemberId(), - TagIdentifier.getEnumId(), - TagIdentifier.getEnumValueId(), - TagIdentifier.getExternFwdDeclarationId(), - TagIdentifier.getFunctionDefinitionId(), - TagIdentifier.getFunctionPrototypeId(), - TagIdentifier.getLocalVariableId(), - TagIdentifier.getMacroId(), - TagIdentifier.getNamespaceId(), - TagIdentifier.getStructId(), - TagIdentifier.getTypedefId(), - TagIdentifier.getUnionId(), - TagIdentifier.getVariableDefinitionId() - ] - self.syntax_highlighter = VimSyntaxHighlighter(self.tag_id_list, self.output_syntax_file) - logging.info("tag_id_list = {0}.".format(self.tag_id_list)) + self.syntax_highlighter = VimSyntaxHighlighter(self.output_syntax_file) - def run_impl(self, filename): + def run_impl(self, args): + contents_filename = str(args[0]) + original_filename = str(args[1]) + compiler_args = list(str(args[2]).split()) + project_root_directory = str(args[3]) start = time.clock() - self.syntax_highlighter.generate_vim_syntax_file(filename) + self.syntax_highlighter.generate_vim_syntax_file_from_clang(contents_filename, compiler_args, project_root_directory) end = time.clock() - logging.info("Generating vim syntax for '{0}' took {1}.".format(filename, end-start)) - YavideUtils.call_vim_remote_function(self.yavide_instance, "Y_SrcCodeHighlighter_Apply('" + filename + "'" + ", '" + self.output_syntax_file + "')") - + logging.info("Generating vim syntax for '{0}' took {1}.".format(original_filename, end-start)) + YavideUtils.call_vim_remote_function(self.yavide_instance, "Y_SrcCodeHighlighter_Apply('" + original_filename + "'" + ", '" + self.output_syntax_file + "')") diff --git a/core/syntax/after/syntax/cpp/cpp_syntax_highlight.vim b/core/syntax/after/syntax/cpp/cpp_syntax_highlight.vim index 4ea594c..a0043e8 100644 --- a/core/syntax/after/syntax/cpp/cpp_syntax_highlight.vim +++ b/core/syntax/after/syntax/cpp/cpp_syntax_highlight.vim @@ -2,17 +2,23 @@ " Language: C++ hi def link yavideCppNamespace NamespaceTag +hi def link yavideCppNamespaceAlias NamespaceAliasTag hi def link yavideCppClass ClassTag hi def link yavideCppStructure StructureTag hi def link yavideCppEnum EnumTag hi def link yavideCppEnumValue EnumValueTag hi def link yavideCppUnion UnionTag -hi def link yavideCppClassStructUnionMember MemberTag +hi def link yavideCppField FieldTag hi def link yavideCppLocalVariable LocalVariableTag -hi def link yavideCppVariableDefinition VariableDefinitionTag -hi def link yavideCppFunctionPrototype FunctionPrototypeTag -hi def link yavideCppFunctionDefinition FunctionDefinitionTag -hi def link yavideCppMacro MacroTag +hi def link yavideCppFunction FunctionTag +hi def link yavideCppMethod MethodTag +hi def link yavideCppFunctionParameter FunctionParameterTag +hi def link yavideCppTemplateTypeParameter TemplateTypeParameterTag +hi def link yavideCppTemplateNonTypeParameter TemplateNonTypeParameterTag +hi def link yavideCppTemplateTemplateParameter TemplateTemplateParameterTag +hi def link yavideCppMacroDefinition MacroDefinitionTag +hi def link yavideCppMacroInstantiation MacroInstantiationTag hi def link yavideCppTypedef TypedefTag -hi def link yavideCppExternForwardDeclaration ExternFwdDeclarationTag +hi def link yavideCppUsingDirective UsingDirectiveTag +hi def link yavideCppUsingDeclaration UsingDeclarationTag diff --git a/docs/services_framework.md b/docs/services_framework.md index a3b4d3a..b61f78b 100644 --- a/docs/services_framework.md +++ b/docs/services_framework.md @@ -30,34 +30,78 @@ implementation details of particular `service`. See existing `services` to see a # Services ## Syntax highlighting -TBD Compared to the limited `Vim` syntax highlighting mechanism, this service brings more complete syntax highlighting including support for the following symbols: * Namespaces +* Namespace aliases * Classes * Structures * Enums * Enum values * Unions -* Class/struct members * Local variables -* Variable definitions -* Function prototypes -* Function definitions -* Macros +* Class/struct/union data members +* Class/struct/union function members +* Functions +* Function parameters +* Template type parameters +* Template non type parameters +* Template template parameters +* Macro definitions +* Macro instantiations * Typedefs -* Forward declarations - -As of now `ctags` is being used as a back-end for syntax highlighting which falls short in some cases which is why it is planned to replace it by the `clang`-based solution to get more complete and more precise results. +* Using directives +* Using declarations Before | After -------|-------- -![Syntax highlighting before](https://raw.githubusercontent.com/wiki/JBakamovic/yavide/images/syntax_highlighting_before.png) | ![Syntax highlighting after](https://raw.githubusercontent.com/wiki/JBakamovic/yavide/images/syntax_highlighting_after.png) +![Syntax highlighting before](https://raw.githubusercontent.com/wiki/JBakamovic/yavide/images/syntax_highlighting_before_clang.png) | ![Syntax highlighting after](https://raw.githubusercontent.com/wiki/JBakamovic/yavide/images/syntax_highlighting_after_clang.png) + +Initially, syntax highlighting service was using `ctags` as a back-end for tokenizing the source code. However, `ctags`, being based on +heuristics and not being a full-featured C++ parser, was falling short in some cases and especially with modern C++ codebases. + +Now, `ctags` back-end has been replaced with `clang`-based one and that gives us much more complete and correct results. Not only that +new back-end is going to be used for syntax highlighting only, but it can/will be reused for many other purposes. I.e. implementing other services +such as class browsers, indexers, refactoring, fix-its, comments, etc. + +In order to see the full effect of syntax highlighting one will need to have a colorscheme which supports additional highlight groups. Standard Vim +colorschemes, if not modified, do **not** contain these. See [`this file`](../core/syntax/after/syntax/cpp/cpp_syntax_highlight.vim) for a list of new +highlight groups which you will need to add to your colorscheme of choice should you want to make a full use of syntax highlighting service. + +There is an [`example colorscheme`](https://github.com/JBakamovic/yaflandia) that I have made and which `Yavide` uses by default. One may have a look there to see how things might look like. +Disclaimer: **I am really not good at colors so feel free contribute back.** + +### Configuration + +As we are now using a full C++ back-end, in order to get the best results, we need to feed it in with compiler flags which are actually used to compile that specific project. + +As of now, user is able to provide those flags (as a whitespace separated list) through `g:project_compiler_args` variable which can be found at `.yavide_proj` +(a file at project root directory; generated by `Yavide` upon creation of a new project). + +Future plan is to automate this step further requiring no user intervention (i.e. support for CMake/Makefile projects). + +Example how configuration might look like is: + + let g:project_compiler_args = '-Ilib -Iinclude -DFEATURE_XX -DEFATURE_YY -Wall -Werror' + +### Diagnostics + +If you don't see what you expected, you should have a look at the log file generated by `Yavide` server (i.e. `/tmp/YAVIDE_server.log`). There will be entries related to the +syntax highlighter which will give you a hint what went wrong (i.e. `libclang` diagnostics). Specifically, you should try to search for a pattern like this: + + [INFO] [clang_tokenizer.py:35] run(): Parsing error: /sleep.hh', line 28, column 10>, spelling "'core/future.hh' file not found"> + +### Vim quirks + +For bigger files, **scrolling** will become really really *slow* if `cursorline` variable is set (in my env set by default). This is a very old and very annoying Vim issue which is even mentioned in `vim-help`. +See `:help 'cursorline'` for more details. + +To circumvent this issue one might `set nocursorline` for big files (can be even scripted) or turn it off permanently by putting it in [`.editor.vimrc`](../core/.editor.vimrc) file. ## Indexing TBD -Similar as with syntax highlighting but in addition `cscope` is also used. +`ctags` + `cscope` combination is used. Shall be replaced with `clang`-based approach. ![Indexer in action](https://raw.githubusercontent.com/wiki/JBakamovic/yavide/images/indexer_in_action.gif) diff --git a/install.sh b/install.sh index f98d2e1..0e345f9 100755 --- a/install.sh +++ b/install.sh @@ -46,12 +46,14 @@ guess_system_package_manager(){ fi if [ $SYSTEM_PACKAGE_TYPE == "rpm" ]; then - SYSTEM_PACKAGE_SET="gvim ctags cscope git wget pcre-devel libyaml-devel python-pip python-devel python-watchdog clang-devel" + SYSTEM_PACKAGE_SET="gvim ctags cscope git wget pcre-devel libyaml-devel python-pip python-devel clang-devel clang-libs" elif [ $SYSTEM_PACKAGE_TYPE == "deb" ]; then SYSTEM_PACKAGE_SET="vim-gnome ctags cscope git wget libpcre3 libpcre3-dev libyaml-dev python-pip python-dev libclang-dev" elif [ $SYSTEM_PACKAGE_TYPE == "archpkg" || $SYSTEM_PACKAGE_TYPE == "ebuild" ]; then - SYSTEM_PACKAGE_SET="gvim ctags cscope git wget pcre libyaml python-pip python python-watchdog clang" + SYSTEM_PACKAGE_SET="gvim ctags cscope git wget pcre libyaml python-pip python clang" fi + + PIP_PACKAGE_SET="clang watchdog" } print_usage(){ @@ -162,10 +164,8 @@ stty $stty_orig # restore terminal setting. ##################################################################################################### echo "$passwd" | sudo -S $SYSTEM_PACKAGE_MANAGER_UPDATE echo "$passwd" | sudo -S $SYSTEM_PACKAGE_MANAGER_INSTALL $SYSTEM_PACKAGE_SET +echo "$passwd" | sudo -S pip install $PIP_PACKAGE_SET -if [ $SYSTEM_PACKAGE_TYPE == "deb" ]; then - echo "$passwd" | sudo -S pip install watchdog -fi mkdir -p $HOME/.fonts && git clone https://github.com/Lokaltog/powerline-fonts.git $HOME/.fonts fc-cache -vf $HOME/.fonts