import os, re from subprocess import Popen, PIPE, STDOUT import color, default, method, method.shell, mode, tab from lex import Grammar, PatternRule, RegionRule, PatternGroupRule, OverridePatternRule from lex import PatternMatchRule 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$'), ] class MacroGrammar(Grammar): rules = [ PatternRule('continuation', r'\\\n$'), PatternRule('name', r'(?<=#define ) *[a-zA-Z_][a-zA-Z0-9_]*'), PatternRule('name', r'(?<=#ifdef ) *[a-zA-Z_][a-zA-Z0-9_]*'), PatternRule('name', r'(?<=#ifndef ) *[a-zA-Z_][a-zA-Z0-9_]*'), PatternRule('name', r'(?<=#undef ) *[a-zA-Z_][a-zA-Z0-9_]*'), PatternRule(r'concat', r'##[a-zA-Z0-9_]+'), PatternRule(r'quoted', r'#[a-zA-Z0-9_]+'), PatternMatchRule(r'xyz', r'(defined)(\()([a-zA-Z_][a-zA-Z0-9_]*)(\))', r'function', r'delimiter', r'name', r'delimiter'), ] class CGrammar(Grammar): rules = [ PatternRule(r'spaces', r' +'), PatternRule(r"delimiter", r"\.|\(|\)|\[|\]|{|}|@|,|:|`|;|=(?!=)|\?|->"), PatternRule(r'eol', r"\n$"), PatternMatchRule(r'x', r'(struct|enum|union)( +)([a-zA-Z_][a-zA-Z0-9_]*)', r'builtin', r'spaces', r'type'), PatternRule(r'builtin', r"(?:break|case|continue|default|do|else|for|goto|if|return|sizeof|switch|while)(?![a-zA-Z_])"), PatternRule(r'builtin', r"(?:signed|register|extern|const|static|enum|struct|typedef|union|unsigned|volatile)(?![a-zA-Z_])"), PatternRule(r'type', r"(?:auto|char|double|float|int|long|short|void|volatile)(?![a-zA-Z_])"), PatternMatchRule(r'x', r'([a-zA-Z_][a-zA-Z0-9_]*)(\**)( +)(\**)([a-zA-Z_][a-zA-Z0-9_]*)', r'type', r'spaces', r'binop', r'spaces', r'identifier'), PatternRule(r'function', r'[a-zA-Z_][a-zA-Z0-9_]*(?= *\()'), PatternRule(r'constant', r"[A-Z_][A-Z0-9_]+(?![a-zA-Z0-9_])"), PatternRule(r'label', r'[a-zA-Z_][a-zA-Z0-9_]*(?=:)'), RegionRule(r'error', r'# *error', ErrorGrammar, r'\n$'), RegionRule(r'macro', r'# *(?:assert|cpu|define|elif|else|endif|error|ident|ifdef|ifndef|if|import|include_next|line|machine|pragma_once|pragma|system|unassert|undef|warning)(?!=[a-zA-Z0-9_])', MacroGrammar, r'\n$'), RegionRule(r'comment', r'/\*', CommentGrammar, r'\*/'), PatternRule(r'comment', r'//.*$'), RegionRule(r'string', '"', StringGrammar2, '"'), PatternRule(r"float", r"-?[0-9]+\.[0-9]*|-?\.[0-9]+|-?(?:[0-9]|[0-9]+\.[0-9]*|-?\.[0-9]+)[eE][\+-]?[0-9]+"), PatternRule(r"integer", r"(?:0(?![x0-9])|-?[1-9][0-9]*|0[0-7]+|0[xX][0-9a-fA-F]+)[lL]?"), PatternRule(r"operator", r"!(?!=)|\+=|-=|\*=|/=|//=|%=|&=\|\^=|>>=|<<=|\*\*="), PatternRule(r'operator', r"\+|<>|<<|<=|<|-|>>|>=|>|\*\*|&|\*|\||/|\^|==|//|~|!=|%"), RegionRule(r'macrocomment', r'#if +(?:0|NULL|FALSE)', Grammar, r'#endif'), PatternRule(r'char', r"'.'|'\\.'|'\\[0-7]{3}'"), PatternMatchRule(r'x', r'(# *include)( +)(.+)(\n|$)', r'macro.start', r'spaces', r'header', r'macro.end'), #PatternGroupRule(r'includegrp', r'macro.start', r'# *include', r'spaces', # #r' +', r'header', r'< *[-A-Za-z/0-9_.]+ *>|" *[-A-Za-z/0-9_.]+ *"', # r' +', r'header', r'< *[-A-Za-z/0-9_.]+ *>|" *[-A-Za-z/0-9_.]+ *"|[A-Za-z0-9_]+', # 'macro.end', r'\n$'), PatternRule(r'identifier', r"[a-zA-Z_][a-zA-Z0-9_]*"), OverridePatternRule(r'comment', r'/\* *@@:(?P[.a-zA-Z0-9_]+):(?P[.a-zA-Z0-9_]+) *\*/$'), OverridePatternRule(r'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': {'}': '{', ')': '(', ']': '['}} control_tokens = {'keyword': {'if': 1, 'else': 1, 'while': 1, 'do': 1, 'for': 1}} end_at_eof = False end_at_tokens = {'delimiter': {';': 1}} nocontinue_tokens = {'delimiter': {';': 1}} start_free_tokens = {'string.start': 'string.end'} end_free_tokens = {'string.end': 'string.start'} start_macro_tokens = {'macro.start': 'macro.end'} end_macro_tokens = {'macro.end': 'macro.start'} def is_base(self, y): if y == 0: return True tokens = self._get_tokens(y) # this assumes that people aren't gonna use these macros inside of # blocks, which is probably ok. t = tokens[0] if t.fqname() == 'macro.start' and t.string in ('#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 # four types of tokens below decl = False for t in tokens: if t.name in ('keyword', 'identifier', 'structname', 'enumname'): decl = True continue if decl and t.name == 'function': break else: decl = False break return decl def _is_indent(self, t): return t.name == 'spaces' def _is_ignored(self, t): return t.fqname() in ('spaces', 'eol', 'comment', 'comment.start', 'comment.data', 'comment.null', 'comment.end') class CCheckSyntax(method.shell.Exec): '''Build this C program (using the mode's make cmd)''' show_success = False args = [] def _execute(self, w, **vargs): if w.application.config['c.syntax-rel-dir']: d = os.path.dirname(w.buffer.path) self._doit(w, w.buffer.path, w.application.config['c.syntax-cmd'], cmdname='c-check-syntax', cmddir=d) else: self._doit(w, w.buffer.path, w.application.config['c.syntax-cmd'], cmdname='c-check-syntax') class CMake(method.shell.Exec): '''Build this C program (using the mode's make cmd)''' show_success = False args = [] def _execute(self, w, **vargs): if w.application.config['c.make-rel-dir']: d = os.path.dirname(w.buffer.path) self._doit(w, w.buffer.path, w.application.config['c.make-cmd'], cmdname='c-make', cmddir=d) else: self._doit(w, w.buffer.path, w.application.config['c.make-cmd'], cmdname='c-make') class C(mode.Fundamental): name = 'C' extensions = ['.c', '.h', '.cpp'] tabbercls = CTabber2 grammar = CGrammar opentokens = ('delimiter',) opentags = {'(': ')', '[': ']', '{': '}'} closetokens = ('delimiter',) closetags = {')': '(', ']': '[', '}': '{'} actions = [CCheckSyntax, CMake] format = "%(flag)s %(bname)-18s (%(mname)s) %(indent)s %(cursor)s/%(mark)s %(perc)s [%(func)s]" commentc = '//' colors = { 'macrocomment.start': ('red', 'default', 'bold'), 'macrocomment.null': ('red', 'default', 'bold'), 'macrocomment.end': ('red', 'default', 'bold'), 'macro': ('blue', 'default', 'bold'), 'macro.start': ('blue', 'default', 'bold'), 'macro.name': ('yellow', 'default', 'bold'), 'macro.null': ('magenta', 'default', 'bold'), 'macro.continued': ('red', 'default', 'bold'), 'macro.delimiter': ('default', 'default', 'bold'), 'macro.concat': ('yellow', 'default', 'bold'), 'macro.quoted': ('yellow', 'default', 'bold'), 'include': ('blue', 'default', 'bold'), 'header': ('green', 'default', 'bold'), 'type': ('cyan', 'default', 'bold'), 'constant': ('yellow', 'default', 'bold'), 'error.start': ('blue', 'default', 'bold'), 'error.data': ('green', 'default', 'bold'), 'error.null': ('green', 'default', 'bold'), } 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_status_names(self): names = mode.Fundamental.get_status_names(self) c = self.window.logical_cursor() names['func'] = self.get_line_function(c.y) return names def get_functions(self): return {} def get_function_names(self): return [] def get_line_function(self, y): return None install = C.install