import os.path from subprocess import Popen, PIPE, STDOUT from method.shell import Exec from mode import Fundamental import tab from lex import Grammar, PatternRule, RegionRule, PatternMatchRule, OverridePatternRule from mode.python import StringGrammar2 class CommentGrammar(Grammar): rules = [ PatternRule(r'data', r'(?:[^\*]|\*(?!/))+'), ] class ErrorGrammar(Grammar): rules = [ PatternRule(r'data', r'[^\\\n]+'), PatternRule('continuation', r'\\\n$'), ] chr1 = '[a-zA-Z_]' chr2 = '[a-zA-Z0-9_]' word = chr1 + chr2 + '*' class MacroGrammar(Grammar): rules = [ PatternRule('continuation', r'\\\n$'), PatternRule('name', '(?<=#define ) *' + word), PatternRule('name', '(?<=#ifdef ) *' + word), PatternRule('name', '(?<=#ifndef ) *' + word), PatternRule('name', '(?<=#undef ) *' + word), PatternRule('concat', '##' + chr2 + '+'), PatternRule('quoted', '#' + chr2 + '+'), PatternMatchRule('x', r'(defined)(\()(' + word + r')(\))', 'function', 'delimiter', 'name', 'delimiter'), ] class CGrammar(Grammar): rules = [ PatternRule('spaces', r' +'), PatternMatchRule('x', r'(\()( *)(' + word + r')(\**)( *)(\))( *)(?=[a-zA-Z0-9_\(])', 'delimiter', 'spaces', 'c.type', 'c.operator', 'spaces', 'delimiter', 'spaces'), PatternRule("delimiter", r"\.|\(|\)|\[|\]|{|}|@|,|:|`|;|=(?!=)|\?|->"), PatternRule('eol', r"\n$"), PatternMatchRule('x', r'(struct|enum|union)( +)(' + word + ')', 'c.builtin', 'spaces', 'c.type'), PatternRule('c.builtin', r"(?:break|case|continue|default|do|else|for|goto|if|return|sizeof|switch|while)(?!" + chr2 + ")"), PatternRule('c.builtin', r"(?:signed|register|extern|const|static|enum|struct|typedef|union|unsigned|volatile)(?!" + chr2 + ")"), PatternRule('c.type', r"(?:auto|char|double|float|int|long|short|void|volatile)(?!" + chr2 + ")"), PatternMatchRule('x', '(' + word + ')( +)(\**)(' + word + ')( *)(\()', 'c.type', 'spaces', 'c.operator', 'c.function', 'spaces', 'delimiter'), PatternMatchRule('x', '(' + word + ')(\*+)( +)(' + word + ')( *)(\()', 'c.type', 'c.operator', 'spaces', 'c.function', 'spaces', 'delimiter'), PatternMatchRule('x', '(' + word + ')( +)(\**)(' + word + ')', 'c.type', 'spaces', 'c.operator', 'c.identifier'), PatternMatchRule('x', '(' + word + ')(\*+)( +)(' + word + ')', 'c.type', 'c.operator', 'spaces', 'c.identifier'), PatternRule('c.function', word + r'(?= *\()'), PatternRule('c.constant', "[A-Z_][A-Z0-9_]+(?!" + chr2 + ")"), PatternRule('c.label', word + '(?=:)'), RegionRule('c.error', '# *error', ErrorGrammar, r'\n$'), RegionRule('c.macro', '# *(?:assert|cpu|define|elif|else|endif|error|ident|ifdef|ifndef|if|import|include_next|line|machine|pragma_once|pragma|system|unassert|undef|warning)(?!=' + chr2 + ')', MacroGrammar, r'\n$'), RegionRule('c.comment', r'/\*', CommentGrammar, r'\*/'), PatternRule('c.comment', '//.*$'), RegionRule('c.string', '"', StringGrammar2, '"'), PatternRule("c.float", r"-?[0-9]+\.[0-9]*|-?\.[0-9]+|-?(?:[0-9]|[0-9]+\.[0-9]*|-?\.[0-9]+)[eE][\+-]?[0-9]+"), PatternRule("c.integer", r"(?:0(?![x0-9])|-?[1-9][0-9]*|0[0-7]+|0[xX][0-9a-fA-F]+)[lL]?"), PatternRule("c.operator", r"!(?!=)|\+=|-=|\*=|/=|//=|%=|&=\|\^=|>>=|<<=|\*\*="), PatternRule('c.operator', r"\+|<>|<<|<=|<|-|>>|>=|>|\*\*|&&|&|\*|\|\||\||/|\^|==|//|~|!=|%"), RegionRule('c.macrocomment', '#if +(?:0|NULL|FALSE)', Grammar, '#endif'), PatternMatchRule('', r"(')(.)(')", 'c.char.start', 'c.char.data', 'c.char.end'), PatternMatchRule('', r"(')(\\.)(')", 'c.char.start', 'c.char.escaped', 'c.char.end'), PatternMatchRule('', r"(')(\\[0-7]{3})(')", 'c.char.start', 'c.char.octal', 'c.char.end'), #PatternRule('c.char', r"'.'|'\\.'|'\\[0-7]{3}'"), PatternMatchRule('x', r'(# *include)( +)(.+)(\n|$)', 'c.macro.start', 'spaces', 'c.header', 'c.macro.end'), PatternRule('c.identifier', word), OverridePatternRule('c.comment', r'/\* *@@:(?P[.a-zA-Z0-9_]+):(?P[.a-zA-Z0-9_]+) *\*/$'), OverridePatternRule('c.comment', r'// *@@:(?P[.a-zA-Z0-9_]+):(?P[.a-zA-Z0-9_]+) *$'), ] MacroGrammar.rules.extend(CGrammar.rules) class CTabber2(tab.StackTabber2): open_tokens = {'delimiter': {'{': '}', '(': ')', '[': ']'}} close_tokens = {'delimiter': {'}': '{', ')': '(', ']': '['}} # NOTE: we actually need at least two kinds of control tokens: # an if/else class, and a do/for/while class. this is so we can get # indenting support for else working correctly. control_tokens = {'c.builtin': {'if': 1, 'else': 1, 'while': 1, 'do': 1, 'for': 1}} end_at_eof = False end_at_tokens = {'delimiter': {';': 1}} nocontinue_tokens = {'delimiter': {';': 1, ',': 1}} start_free_tokens = {'c.string.start': 'c.string.end'} end_free_tokens = {'c.string.end': 'c.string.start'} start_macro_tokens = {'c.macro.start': 'c.macro.end'} end_macro_tokens = {'c.macro.end': 'c.macro.start'} def _is_base(self, y): if y == 0: return True # if there are no tokens we don't really have any info tokens = self._get_tokens(y) if not tokens: return False # this assumes that people aren't gonna use these macros inside of # blocks, which is probably ok. t = tokens[0] if t.fqmatchs('c.macro.start', ('#define', '#include')): return True # detecting function declarations is annoying; this assumes that people # won't put a variable type and name on different lines, but that they # might do that for function return type and name. # # unfortunately, valid function return types might include any of the # types of tokens below decl = False for t in tokens: if decl and self._is_ignored(t): pass elif t.isa('c.keyword', 'c.identifier', 'c.type'): decl = True elif decl and t.name == 'c.function': break else: decl = False break return decl def _is_indent(self, t): return t.name == 'spaces' def _is_ignored(self, t): return t.fqisa('spaces', 'eol', 'c.comment', 'c.comment.start', 'c.comment.data', 'c.comment.null', 'c.comment.end') class CCheckSyntax(Exec): '''Build this C program (using the mode's make cmd)''' show_success = False args = [] def _execute(self, w, **vargs): cmd='C_INCLUDE_PATH=. %s' % w.application.config['c.syntax-cmd'] if w.application.config['c.syntax-rel-dir']: self._doit(w, w.buffer.path, cmd, cmdname='c-check-syntax', cmddir=os.path.dirname(w.buffer.path)) else: self._doit(w, w.buffer.path, cmd, cmdname='c-check-syntax') class CMake(Exec): '''Build this C program (using the mode's make cmd)''' show_success = False args = [] def _execute(self, w, **vargs): cmd = w.application.config['c.make-cmd'], if w.application.config['c.make-rel-dir']: d = os.path.dirname(w.buffer.path) self._doit(w, w.buffer.path, cmd, cmdname='c-make', cmddir=d) else: self._doit(w, w.buffer.path, cmd, cmdname='c-make') # white is for delimiters, operators, numbers default = ('default', 'default') # magenta is for keywords/builtins lo_magenta = ('magenta202', 'default') hi_magenta = ('magenta505', 'default') # red is for comments lo_red = ('red300', 'default') hi_red = ('red511', 'default') # orange is for macro definitions, headers and constants hi_orange = ('yellow531', 'default') lo_orange = ('yellow520', 'default') # yellow is for parts of macros hi_yellow = ('yellow551', 'default') lo_yellow = ('yellow330', 'default') # green is for strings and characters lo_green = ('green030', 'default') hi_green = ('green050', 'default') # cyan is for types lo_cyan = ('cyan033', 'default') hi_cyan = ('cyan155', 'default') # blue is definitions, functions and some macros lo_blue = ('blue113', 'default') hi_blue = ('blue225', 'default') class C(Fundamental): name = 'C' extensions = ['.c', '.h', '.cpp'] tabbercls = CTabber2 grammar = CGrammar opentokens = ('delimiter',) opentags = {'(': ')', '[': ']', '{': '}'} closetokens = ('delimiter',) closetags = {')': '(', ']': '[', '}': '{'} actions = [CCheckSyntax, CMake] format = "%(flag)s %(bname)s (%(mname)s) %(indent)s %(cursor)s %(perc)s [%(func)s]" commentc = '//' colors = { 'c.comment': hi_red, 'c.comment.start': hi_red, 'c.comment.data': hi_red, 'c.comment.end': hi_red, 'c.macrocomment.start': hi_red, 'c.macrocomment.null': hi_red, 'c.macrocomment.end': hi_red, 'c.macro': hi_blue, 'c.macro.start': hi_blue, 'c.macro.name': hi_orange, 'c.macro.null': hi_magenta, 'c.macro.concat': hi_yellow, 'c.macro.quoted': hi_green, 'c.error.start': hi_blue, 'c.error.data': hi_green, 'c.char.start': lo_green, 'c.char.end': lo_green, 'c.char.data': hi_green, 'c.char.escaped': hi_magenta, 'c.char.octal': hi_magenta, 'c.type': hi_cyan, 'c.include': hi_blue, 'c.header': lo_orange, 'c.constant': hi_orange, } config = { 'c.syntax-cmd': "gcc -x c -fsyntax-only %(path)s", 'c.syntax-rel-dir': False, 'c.make-cmd': "make", 'c.make-rel-dir': True, } lconfig = { 'ignore_suffix': ['.o'], } _bindings = { 'close-paren': (')',), 'close-brace': ('}',), 'close-bracket': (']',), 'c-check-syntax': ('C-c s',), 'c-make': ('C-c C-c',), } def get_functions(self): return {} def get_function_names(self): return [] def get_line_function(self, y): return None def get_status_names(self): names = Fundamental.get_status_names(self) c = self.window.logical_cursor() names['func'] = self.get_line_function(c.y) return names install = C.install