├── .gitignore ├── Context.sublime-menu ├── CscopeSublime.sublime-settings ├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap ├── Default.sublime-commands ├── Lookup Results.hidden-tmLanguage ├── Main.sublime-menu ├── README.md ├── Side Bar.sublime-menu └── cscope.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.cache -------------------------------------------------------------------------------- /Context.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "-", "id": "cscope" 4 | }, 5 | { 6 | "caption": "Cscope: Look up symbol", 7 | "command": "cscope", 8 | "args": {"mode": 0} 9 | }, 10 | { 11 | "caption": "Cscope: Look up function definition", 12 | "command": "cscope", 13 | "args": {"mode": 1} 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /CscopeSublime.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // All options in here can also be specified in your user settings 3 | // with the prefix "CscopeSublime_". 4 | // For example if you have 5 | // 6 | // { 7 | // "CscopeSublime_display_outline": false 8 | // } 9 | // 10 | // in your user settings, this will override the settings specified 11 | // in this file. 12 | 13 | // Whether or not to draw an outline around the matched symbol in the lookup results 14 | "display_outline": true, 15 | 16 | // Whether to prompt the user to edit/confirm the symbol before searching 17 | "prompt_before_searching": true, 18 | 19 | // The "cscope" program executable name. If needed, you can change this 20 | // in your user settings to be the full path to the cscope executable. 21 | // If you leave this as the default (just "cscope"), then your system 22 | // $PATH (or %PATH% for Windows users) will be searched. 23 | // 24 | // Linux and OS X example: "executable": "/usr/bin/cscope" 25 | // Windows example: "executable": "C:\\cscope\\cscope.exe" 26 | "executable": "cscope", 27 | 28 | // A location for the cscope database - this will be used in preference to any 'found' database 29 | //"database_location": "D:\\Program Files\\cscope\\cscope.out", 30 | 31 | // An optional custom command used to build cscope's database. 32 | // This must be in array format. 33 | //"database_build_command": [ "cscope-indexer", "-r" ], 34 | } 35 | -------------------------------------------------------------------------------- /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["ctrl+\\"], 4 | "command": "show_overlay", 5 | "args": 6 | { 7 | "overlay": "command_palette", 8 | "text": "Cscope: " 9 | } 10 | }, 11 | { 12 | // Lookup symbol 13 | "keys": ["ctrl+l", "ctrl+s"], 14 | "command": "cscope", 15 | "args": 16 | { 17 | "mode": 0 18 | } 19 | 20 | }, 21 | { 22 | // Lookup definition 23 | "keys": ["ctrl+l", "ctrl+d"], 24 | "command": "cscope", 25 | "args": 26 | { 27 | "mode": 1 28 | } 29 | }, 30 | { 31 | // Lookup CalleEs 32 | "keys": ["ctrl+l", "ctrl+e"], 33 | "command": "cscope", 34 | "args": 35 | { 36 | "mode": 2 37 | } 38 | }, 39 | { 40 | // Lookup CalleRs 41 | "keys": ["ctrl+l", "ctrl+r"], 42 | "command": "cscope", 43 | "args": 44 | { 45 | "mode": 3 46 | } 47 | }, 48 | { 49 | // Navigate to results with enter key 50 | "keys": ["enter"], 51 | "command": "cscope_visiter", 52 | "context": [{"key": "selector", "operand": "text.find-in-files"}] 53 | }, 54 | { 55 | "keys": ["ctrl+shift+["], 56 | "command": "goback" 57 | }, 58 | { 59 | "keys": ["ctrl+shift+]"], 60 | "command": "forward" 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["super+\\"], 4 | "command": "show_overlay", 5 | "args": 6 | { 7 | "overlay": "command_palette", 8 | "text": "Cscope: " 9 | } 10 | }, 11 | { 12 | // Lookup symbol 13 | "keys": ["super+l", "super+s"], 14 | "command": "cscope", 15 | "args": 16 | { 17 | "mode": 0 18 | } 19 | 20 | }, 21 | { 22 | // Lookup definition 23 | "keys": ["super+l", "super+d"], 24 | "command": "cscope", 25 | "args": 26 | { 27 | "mode": 1 28 | } 29 | }, 30 | { 31 | // Lookup CalleEs 32 | "keys": ["super+l", "super+e"], 33 | "command": "cscope", 34 | "args": 35 | { 36 | "mode": 2 37 | } 38 | }, 39 | { 40 | // Lookup CalleRs 41 | "keys": ["super+l", "super+r"], 42 | "command": "cscope", 43 | "args": 44 | { 45 | "mode": 3 46 | } 47 | }, 48 | { 49 | // Navigate to results with enter key 50 | "keys": ["enter"], 51 | "command": "cscope_visiter", 52 | "context": [{"key": "selector", "operand": "text.find-in-files"}] 53 | }, 54 | { 55 | "keys": ["super+shift+["], 56 | "command": "goback" 57 | }, 58 | { 59 | "keys": ["super+shift+]"], 60 | "command": "forward" 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "keys": ["ctrl+\\"], 4 | "command": "show_overlay", 5 | "args": 6 | { 7 | "overlay": "command_palette", 8 | "text": "Cscope: " 9 | } 10 | }, 11 | { 12 | // Lookup symbol 13 | "keys": ["ctrl+l", "ctrl+s"], 14 | "command": "cscope", 15 | "args": 16 | { 17 | "mode": 0 18 | } 19 | 20 | }, 21 | { 22 | // Lookup definition 23 | "keys": ["ctrl+l", "ctrl+d"], 24 | "command": "cscope", 25 | "args": 26 | { 27 | "mode": 1 28 | } 29 | }, 30 | { 31 | // Lookup CalleEs 32 | "keys": ["ctrl+l", "ctrl+e"], 33 | "command": "cscope", 34 | "args": 35 | { 36 | "mode": 2 37 | } 38 | }, 39 | { 40 | // Lookup CalleRs 41 | "keys": ["ctrl+l", "ctrl+r"], 42 | "command": "cscope", 43 | "args": 44 | { 45 | "mode": 3 46 | } 47 | }, 48 | { 49 | // Navigate to results with enter key 50 | "keys": ["enter"], 51 | "command": "cscope_visiter", 52 | "context": [{"key": "selector", "operand": "text.find-in-files"}] 53 | }, 54 | { 55 | "keys": ["ctrl+shift+["], 56 | "command": "goback" 57 | }, 58 | { 59 | "keys": ["ctrl+shift+]"], 60 | "command": "forward" 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Cscope: Look up symbol", 4 | "command": "cscope", 5 | "args": { "mode": 0 } 6 | }, 7 | { 8 | "caption": "Cscope: Look up global definition", 9 | "command": "cscope", 10 | "args": { "mode": 1 } 11 | }, 12 | { 13 | "caption": "Cscope: Look up functions called by this function", 14 | "command": "cscope", 15 | "args": { "mode": 2 } 16 | }, 17 | { 18 | "caption": "Cscope: Look up functions calling this function", 19 | "command": "cscope", 20 | "args": { "mode": 3 } 21 | }, 22 | { 23 | "caption": "Cscope: Search for text string", 24 | "command": "cscope", 25 | "args": { "mode": 4 } 26 | }, 27 | { 28 | "caption": "Cscope: Search using egrep pattern", 29 | "command": "cscope", 30 | "args": { "mode": 6 } 31 | }, 32 | { 33 | "caption": "Cscope: Find file", 34 | "command": "cscope", 35 | "args": { "mode": 7 } 36 | }, 37 | { 38 | "caption": "Cscope: Find files #including a file", 39 | "command": "cscope", 40 | "args": { "mode": 8 } 41 | }, 42 | { 43 | "caption": "Cscope: Rebuild database", 44 | "command": "cscope", 45 | "args": { "mode": "database_rebuild" } 46 | } 47 | ] 48 | -------------------------------------------------------------------------------- /Lookup Results.hidden-tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Lookup Results 7 | 8 | patterns 9 | 10 | 11 | match 12 | (.*):$ 13 | captures 14 | 15 | 1 16 | 17 | name 18 | entity.name.filename.find-in-files 19 | 20 | 21 | 22 | 23 | match 24 | ^\s*([0-9]+)\s+(\[\S+: \S+\]) 25 | captures 26 | 27 | 1 28 | 29 | name 30 | constant.numeric.line-number.find-in-files 31 | 32 | 2 33 | 34 | name 35 | keyword 36 | 37 | 38 | 39 | 40 | scopeName 41 | text.find-in-files 42 | 43 | 44 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences", 4 | "mnemonic": "n", 5 | "id": "preferences", 6 | "children": 7 | [ 8 | { 9 | "caption": "Package Settings", 10 | "mnemonic": "P", 11 | "id": "package-settings", 12 | "children": 13 | [ 14 | { 15 | "caption": "CscopeSublime", 16 | "children": 17 | [ 18 | { 19 | "command": "open_file", 20 | "args": {"file": "${packages}/Cscope/CscopeSublime.sublime-settings"}, 21 | "caption": "Settings – Default" 22 | }, 23 | { 24 | "command": "open_file", 25 | "args": {"file": "${packages}/User/CscopeSublime.sublime-settings"}, 26 | "caption": "Settings – User" 27 | } 28 | ] 29 | } 30 | ] 31 | } 32 | ] 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOTICE 2 | 3 | Looking for a new maintainer for CscopeSublime! Both of our current maintainers are no longer able to contribute actively to this plugin. If you care about CscopeSublime and would love to see it taken care of as much as we would, please reach out to us and let us know via an issue report. 4 | 5 | Alternately, here are a list of forks of this repo that might perhaps be actively maintained: https://github.com/ameyp/CscopeSublime/network/members 6 | 7 | # Cscope 8 | A plugin to use Cscope for code navigation from inside Sublime Text 2 and 3. 9 | 10 | ## Features 11 | This plugin supports the majority of the functionality offered by Cscope, namely: 12 | 13 | 1. Find a given symbol 14 | 2. Find a given function definition 15 | 3. Find functions called by a given function 16 | 4. Find functions calling a given function 17 | 5. Find a given text string 18 | 6. Find a given egrep pattern 19 | 7. Find a given file 20 | 8. Find files #including a given file 21 | 22 | This plugin also allows the user to rebuild the Cscope database from inside Sublime Text. 23 | 24 | ## Installation 25 | 1. Install Cscope (a Windows port can be found [here](http://code.google.com/p/cscope-win32)) 26 | 2. Customize the cscope executable path as explained in the Configuration section below, if needed. 27 | 3. Generate a cscope database (cscope.out) in the root directory of your project 28 | 4. Check out the repo under your "Packages" directory or install via [Package Control](http://wbond.net/sublime_packages/package_control) and restart Sublime Text. 29 | 30 | ## Screenshots 31 | Here's what the symbol lookup results buffer looks like: 32 | ![find-results-new-3](https://f.cloud.github.com/assets/83116/243889/94dd1c70-8a56-11e2-9c4b-3fc0b2beb36a.png) 33 | 34 | ## Configuration 35 | If you wish to change the way CscopeSublime behaves, you have two options: 36 | 37 | 1. Modify the corresponding setting in the default CscopeSublime.sublime-settings file in the package's directory 38 | 2. Add a setting in your `Settings - User` file prefixed with `CscopeSublime_`. 39 | For example, to modify the `display_outline` setting and set it to `false`, put the line `"CscopeSublime_display_outline": false` in your settings file. 40 | 41 | ## Keybindings 42 | 43 | - `Ctrl/Super + \` - Show Cscope options 44 | - `Ctrl/Super + L``Ctrl/Super + S` - Look up symbol under cursor 45 | - `Ctrl/Super + L``Ctrl/Super + D` - Look up definition under cursor 46 | - `Ctrl/Super + L``Ctrl/Super + E` - Look up functions called by the function under the cursor 47 | - `Ctrl/Super + L``Ctrl/Super + R` - Look up functions calling the function under the cursor 48 | - `Ctrl/Super + Shift + [` - Jump back 49 | - `Ctrl/Super + Shift + ]` - Jump forward 50 | 51 | ## Notes 52 | The plugin will recursively search for the cscope database in parent directories of the currently open file until it either finds the database or reaches the root directory. 53 | 54 | ## License 55 | This whole package is distributed under the MIT license. 56 | -------------------------------------------------------------------------------- /Side Bar.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Cscope: Rebuild database", 4 | "command": "cscope", 5 | "args": { "mode": "database_rebuild" } 6 | }, 7 | { 8 | "caption": "-", "id": "cscope" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /cscope.py: -------------------------------------------------------------------------------- 1 | import sublime, sublime_plugin 2 | import os 3 | import re 4 | import subprocess 5 | import string 6 | import threading 7 | import errno 8 | 9 | CSCOPE_PLUGIN_DIR = os.path.basename(os.path.dirname(os.path.realpath(__file__))) 10 | 11 | # Package Control on ST3 compresses the package into a single "package-name.sublime-package" file, 12 | # but ST3 internally treats the location of the package's contents as being in "Packages/packages-name/" 13 | if CSCOPE_PLUGIN_DIR.find(".sublime-package") != -1: 14 | CSCOPE_PLUGIN_DIR = CSCOPE_PLUGIN_DIR[0:CSCOPE_PLUGIN_DIR.find(".sublime-package")] 15 | 16 | CSCOPE_SYNTAX_FILE = "Packages/" + CSCOPE_PLUGIN_DIR + "/Lookup Results.hidden-tmLanguage" 17 | CSCOPE_SEARCH_MODES = { 18 | 0: "C symbol", 19 | 1: "global definition", 20 | 2: "functions called by this function", 21 | 3: "functions calling this function", 22 | 4: "text string", 23 | # intentionally not providing 5/Change this text string 24 | 6: "egrep pattern", 25 | 7: "file named", 26 | 8: "files #including this file" 27 | } 28 | CSCOPE_CMD_DATABASE_REBUILD = "database_rebuild" 29 | 30 | def get_settings(): 31 | return sublime.load_settings("CscopeSublime.sublime-settings") 32 | 33 | def get_setting(key, default=None, view=None): 34 | try: 35 | if view == None: 36 | view = sublime.active_window().active_view() 37 | s = view.settings() 38 | if s.has("CscopeSublime_%s" % key): 39 | return s.get("CscopeSublime_%s" % key) 40 | except: 41 | pass 42 | return get_settings().get(key, default) 43 | 44 | 45 | class CscopeDatabase(object): 46 | def __init__(self, view, executable): 47 | self.view = view 48 | self.executable = executable 49 | self.root = None 50 | self.location = None 51 | 52 | def update_location(self, filename): 53 | self.root = None 54 | self.location = None 55 | project_info = None 56 | project_info_paths = [] 57 | 58 | # project_data is only available in ST3. we need to add this check 59 | # to not break our plugin for ST2 users. see also issue #51. 60 | if hasattr(self.view.window(), 'project_data'): 61 | project_info = self.view.window().project_data() 62 | if project_info and 'folders' in project_info: 63 | project_info_paths = [folder['path'] for folder in project_info['folders']] 64 | 65 | # if the user provided database_location, validate it before using it 66 | database_location = get_setting('database_location') 67 | 68 | if database_location: 69 | print('CScopeDatabase: user provided database_location: {}' 70 | .format(database_location)) 71 | 72 | # if it's not a string, it's not valid 73 | if not isinstance(database_location, str): 74 | sublime.error_message('Cscope: Invalid database_location: {}' 75 | .format(database_location)) 76 | return 77 | 78 | # expand user directory if it was used 79 | if database_location.startswith('~'): 80 | database_location = os.path.expanduser(database_location) 81 | 82 | # if the user-provided database does not end with "cscope.out", this 83 | # is an invalid location 84 | if not database_location.endswith('cscope.out'): 85 | sublime.error_message('Cscope: Invalid database_location: {}. ' 86 | 'Filename must end with cscope.out.' 87 | .format(database_location)) 88 | return 89 | 90 | database_location_dir = os.path.dirname(database_location) 91 | 92 | # the directory _must_ exist because we use this as cwd for subprocess 93 | # commands 94 | if not os.path.isdir(database_location_dir): 95 | sublime.error_message('Cscope: Invalid database_location: {}. ' 96 | 'Directory does not exist.' 97 | .format(database_location)) 98 | return 99 | 100 | self.location = database_location 101 | self.root = database_location_dir 102 | 103 | print('CScopeDatabase: using location: {}, root: {}' 104 | .format(self.location, self.root)) 105 | return 106 | else: 107 | cdir_list = [] 108 | if (filename): 109 | cdir_list = [os.path.dirname(filename)] 110 | elif (project_info): 111 | cdir_list = project_info_paths 112 | else: 113 | print('CscopeDatabase: No project or filename.') 114 | return 115 | 116 | for cdir in cdir_list: 117 | while cdir != os.path.dirname(cdir): 118 | if ("cscope.out" in os.listdir(cdir)): 119 | self.root = cdir 120 | self.location = os.path.join(cdir, "cscope.out") 121 | print('CscopeDatabase: Database found: ', self.location) 122 | return 123 | cdir = os.path.dirname(cdir) 124 | 125 | # If we haven't found an existing database location and root 126 | # directory, and the user has a project defined for this, and 127 | # there's only one path in that project, let's just assume that that 128 | # path is the root of the project. This should be a safe assumption 129 | # and should allow us to create an initial database, if one doesn't 130 | # exist, in a sane place. 131 | if self.root is None and self.location is None and len(project_info_paths) == 1: 132 | self.root = project_info_paths[0] 133 | print('CscopeDatabase: Database not found but setting root: {}' 134 | .format(self.root)) 135 | 136 | def rebuild(self): 137 | if not (self.root and os.path.isdir(self.root)): 138 | sublime.error_message('Cscope: No working directory found. ' 139 | 'Unable to rebuild database.') 140 | return 141 | 142 | cscope_arg_list = get_setting('database_build_command') 143 | 144 | # If the user provided a custom command to build their cscope database, 145 | # use it, otherwise use a hopefully sane default 146 | if not (cscope_arg_list and isinstance(cscope_arg_list, list)): 147 | cscope_arg_list = [self.executable, '-Rbq'] 148 | 149 | print('CscopeDatabase: Rebuilding database in directory: {}, using command: {}' 150 | .format(self.root, cscope_arg_list)) 151 | 152 | popen_arg_list = { 153 | "shell": False, 154 | "stdout": subprocess.PIPE, 155 | "stderr": subprocess.PIPE, 156 | "cwd": self.root 157 | } 158 | 159 | try: 160 | proc = subprocess.Popen(cscope_arg_list, **popen_arg_list) 161 | except OSError as e: 162 | sublime.error_message('Cscope ERROR. Command: {} failed! Error: {}' 163 | .format(cscope_arg_list, e)) 164 | 165 | output, erroroutput = proc.communicate() 166 | print('CscopeDatabase: Rebuild done.') 167 | 168 | 169 | class CscopeVisiter(sublime_plugin.TextCommand): 170 | def __init__(self, view): 171 | self.view = view 172 | 173 | def run(self, edit): 174 | if self.view.settings().get('syntax') == CSCOPE_SYNTAX_FILE: 175 | root_re = re.compile(r'In folder (.+)') 176 | filepath_re = re.compile(r'^(.+):$') 177 | filename_re = re.compile(r'([a-zA-Z0-9_\-\.]+):') 178 | linenum_re = re.compile(r'^\s*([0-9]+)') 179 | 180 | m = root_re.search(self.view.substr(self.view.line(0))) 181 | if not m: 182 | print("Unable to determine root for: %s" % (self.view.substr(self.view.line(0)))) 183 | return 184 | 185 | root = m.group(1) 186 | for region in self.view.sel(): 187 | # Find anything looking like file in whole line at cursor 188 | if not region.empty(): 189 | break 190 | 191 | match_line = self.view.substr(self.view.line(region)) 192 | 193 | re_match_linenum = linenum_re.search(match_line) 194 | re_match_filepath = filepath_re.search(match_line) 195 | 196 | if not re_match_linenum and not re_match_filepath: 197 | print("Unable to match line number or file path in " + match_line) 198 | return 199 | 200 | # if this line had a line number, use it and look up for the filename 201 | if re_match_linenum: 202 | lineno = re_match_linenum.group(1) 203 | line_beg = self.view.line(region).begin() 204 | prev_line_bounds = self.view.line(sublime.Region(line_beg - 1, line_beg - 1)) 205 | file_line = self.view.substr(prev_line_bounds) 206 | 207 | re_match_filepath = filepath_re.search(file_line) 208 | 209 | while re_match_filepath == None: 210 | line_beg = prev_line_bounds.begin() 211 | prev_line_bounds = self.view.line(sublime.Region(line_beg - 1, line_beg - 1)) 212 | file_line = self.view.substr(prev_line_bounds) 213 | re_match_filepath = filepath_re.search(file_line) 214 | 215 | if not re_match_filepath: 216 | print("Unable to match filepath in " + file_line) 217 | return 218 | 219 | elif re_match_filepath: 220 | lineno = "1" 221 | file_line = match_line 222 | 223 | filepath = os.path.join(root, re_match_filepath.group(1)) 224 | if not ( os.path.isfile(filepath) ): 225 | print("Unable to open file: %s" % (filepath)) 226 | return 227 | 228 | re_match_filename = filename_re.search(file_line) 229 | if not re_match_filename: 230 | print("Matched filepath, file exists, but unable to match filename in " + file_line) 231 | return 232 | 233 | filename = re_match_filename.group(1) 234 | print("Opening file '%s'" % (filepath + ":" + lineno)) 235 | CscopeCommand.add_to_history( getEncodedPosition(filepath, lineno) ) 236 | sublime.active_window().open_file(filepath + ":" + lineno, sublime.ENCODED_POSITION) 237 | 238 | 239 | class GobackCommand(sublime_plugin.TextCommand): 240 | def __init__(self, view): 241 | self.view = view 242 | 243 | def run(self, edit): 244 | if not CscopeCommand.is_history_empty(): 245 | file_name = CscopeCommand.pop_latest_from_history() 246 | while file_name == getCurrentPosition(self.view): 247 | file_name = CscopeCommand.pop_latest_from_history() 248 | 249 | CscopeCommand.add_to_future( getCurrentPosition(self.view) ) 250 | sublime.active_window().open_file(file_name, sublime.ENCODED_POSITION) 251 | 252 | 253 | class ForwardCommand(sublime_plugin.TextCommand): 254 | def __init__(self, view): 255 | self.view = view 256 | 257 | def run(self, edit): 258 | if not CscopeCommand.is_future_empty(): 259 | file_name = CscopeCommand.pop_latest_from_future() 260 | while file_name == getCurrentPosition(self.view): 261 | file_name = CscopeCommand.pop_latest_from_future() 262 | 263 | CscopeCommand.add_to_history( getCurrentPosition(self.view) ) 264 | sublime.active_window().open_file(file_name, sublime.ENCODED_POSITION) 265 | 266 | def getEncodedPosition(file_name, line_num): 267 | return file_name + ":" + str(line_num) 268 | 269 | def getCurrentPosition(view): 270 | if view.file_name(): 271 | return getEncodedPosition( view.file_name(), view.rowcol( view.sel()[0].a )[0] + 1 ) 272 | else: 273 | return None 274 | 275 | 276 | class CscopeSublimeDatabaseRebuildWorker(threading.Thread): 277 | def __init__(self, database): 278 | super(CscopeSublimeDatabaseRebuildWorker, self).__init__() 279 | self.database = database 280 | 281 | def run(self): 282 | self.database.rebuild() 283 | 284 | 285 | class CscopeSublimeSearchWorker(threading.Thread): 286 | def __init__(self, view, platform, database, symbol, mode, executable): 287 | super(CscopeSublimeSearchWorker, self).__init__() 288 | self.view = view 289 | self.platform = platform 290 | self.database = database 291 | self.symbol = symbol 292 | self.mode = mode 293 | self.executable = executable 294 | self.output = "" 295 | 296 | # switch statement for the different formatted output 297 | # of Cscope's matches. 298 | def append_match_string(self, match, command_mode, nested): 299 | match_string = "{0}".format(match["file"]) 300 | if command_mode in [0, 4, 6, 8]: 301 | if nested: 302 | match_string = ("{0:>6}\n{1:>6} [scope: {2}] {3}").format("..", match["line"], match["scope"], match["instance"]) 303 | else: 304 | match_string = ("\n{0}:\n{1:>6} [scope: {2}] {3}").format(match["file"].replace(self.database.root, "."), match["line"], match["scope"], match["instance"]) 305 | elif command_mode == 1: 306 | if nested: 307 | match_string = ("{0:>6}\n{1:>6} {2}").format("..", match["line"], match["instance"]) 308 | else: 309 | match_string = ("\n{0}:\n{1:>6} {2}").format(match["file"].replace(self.database.root, "."), match["line"], match["instance"]) 310 | elif command_mode in [2, 3]: 311 | if nested: 312 | match_string = ("{0:>6}\n{1:>6} [function: {2}] {3}").format("..", match["line"], match["function"], match["instance"]) 313 | else: 314 | match_string = ("\n{0}:\n{1:>6} [function: {2}] {3}").format(match["file"].replace(self.database.root, "."), match["line"], match["function"], match["instance"]) 315 | elif command_mode == 7: 316 | match_string = ("\n{0}:").format(match["file"].replace(self.database.root, ".")) 317 | 318 | return match_string 319 | 320 | 321 | def match_output_line(self, line, mode): 322 | match = None 323 | output = None 324 | 325 | # set up RegEx for matching cscope results 326 | if mode in [0, 4, 6, 7, 8]: 327 | match = re.match('(\S+?)\s+?(|\S+)?\s+(\d+)\s+(.+)', line) 328 | if match: 329 | output = { 330 | "file": match.group(1), 331 | "scope": match.group(2), 332 | "line": match.group(3), 333 | "instance": match.group(4) 334 | } 335 | elif mode == 1: 336 | match = re.match('(\S+?)\s+?\S+\s+(\d+)\s+(.+)', line) 337 | if match: 338 | output = { 339 | "file": match.group(1), 340 | "line": match.group(2), 341 | "instance": match.group(3) 342 | } 343 | elif mode in [2, 3]: 344 | # [path] [function] [line #] [string] 345 | match = re.match('(\S+)\s+?(\S+)\s+(\d+)\s+(.+)', line) 346 | if match: 347 | output = { 348 | "file": match.group(1), 349 | "function": match.group(2), 350 | "line": match.group(3), 351 | "instance": match.group(4) 352 | } 353 | 354 | return output 355 | 356 | def run_cscope(self, mode, word): 357 | newline = '\n' 358 | 359 | cscope_arg_list = [self.executable, '-dL', '-f', self.database.location, '-' + str(mode) + word] 360 | popen_arg_list = { 361 | "shell": False, 362 | "stdout": subprocess.PIPE, 363 | "stderr": subprocess.PIPE, 364 | "cwd": self.database.root, 365 | "universal_newlines": True 366 | } 367 | if (self.platform == "windows"): 368 | popen_arg_list["creationflags"] = 0x08000000 369 | popen_arg_list["stdin"] = subprocess.PIPE 370 | 371 | try: 372 | proc = subprocess.Popen(cscope_arg_list, **popen_arg_list) 373 | except OSError as e: 374 | sublime.error_message('Cscope ERROR. Command: {} failed! Error: {}' 375 | .format(cscope_arg_list, e)) 376 | 377 | output, erroroutput = proc.communicate() 378 | 379 | output = output.split(newline) 380 | 381 | self.matches = [] 382 | for i in output: 383 | match = self.match_output_line(i, mode) 384 | if match != None: 385 | self.matches.append(match) 386 | # print "File ", match.group(1), ", Line ", match.group(2), ", Instance ", match.group(3) 387 | 388 | options = [] 389 | prev_file = "" 390 | for match in self.matches: 391 | options.append(self.append_match_string(match, mode, prev_file == match["file"])) 392 | prev_file = match["file"] 393 | 394 | return options 395 | 396 | def run(self): 397 | matches = self.run_cscope(self.mode, self.symbol) 398 | self.num_matches = len(matches) 399 | self.output = "In folder " + self.database.root + \ 400 | "\nFound " + str(len(matches)) + " matches for " + CSCOPE_SEARCH_MODES[self.mode] + \ 401 | ": " + self.symbol + "\n" + 50*"-" + "\n\n" + "\n".join(matches) 402 | 403 | 404 | class CscopeCommand(sublime_plugin.TextCommand): 405 | _backLines = [] 406 | _forwardLines = [] 407 | 408 | cscope_output_info = {} 409 | 410 | @staticmethod 411 | def is_history_empty(): 412 | return len(CscopeCommand._backLines) == 0 413 | 414 | @staticmethod 415 | def add_to_history(line): 416 | print("add_to_history") 417 | print(CscopeCommand._backLines, CscopeCommand._forwardLines) 418 | if CscopeCommand.is_history_empty() or CscopeCommand._backLines[0] != line: 419 | CscopeCommand._backLines.insert(0, line) 420 | if len(CscopeCommand._backLines) > 100: 421 | CscopeCommand._backLines = CscopeCommand._backLines[:100] 422 | 423 | @staticmethod 424 | def pop_latest_from_history(): 425 | print("pop_latest_from_history") 426 | print(CscopeCommand._backLines, CscopeCommand._forwardLines) 427 | latest = CscopeCommand._backLines[0] 428 | CscopeCommand._backLines = CscopeCommand._backLines[1:] 429 | return latest 430 | 431 | @staticmethod 432 | def is_future_empty(): 433 | return len(CscopeCommand._forwardLines) == 0 434 | 435 | @staticmethod 436 | def add_to_future(line): 437 | print("add_to_future") 438 | print(CscopeCommand._backLines, CscopeCommand._forwardLines) 439 | if CscopeCommand.is_future_empty() or CscopeCommand._forwardLines[0] != line: 440 | CscopeCommand._forwardLines.insert(0, line) 441 | if len(CscopeCommand._forwardLines) > 100: 442 | CscopeCommand._forwardLines = CscopeCommand._forwardLines[:100] 443 | 444 | @staticmethod 445 | def pop_latest_from_future(): 446 | print("pop_latest_from_future") 447 | print(CscopeCommand._backLines, CscopeCommand._forwardLines) 448 | latest = CscopeCommand._forwardLines[0] 449 | CscopeCommand._forwardLines = CscopeCommand._forwardLines[1:] 450 | return latest 451 | 452 | def __init__(self, view): 453 | self.view = view 454 | self.database = None 455 | self.executable = None 456 | self.workers = [] 457 | settings = get_settings() 458 | 459 | def update_status(self, count=0, dir=1): 460 | count = count + dir 461 | workInProgress = False 462 | 463 | for worker in self.workers: 464 | if worker.is_alive(): 465 | workInProgress = True 466 | if count == 7: 467 | dir = -1 468 | elif count == 0: 469 | dir = 1 470 | 471 | statusSearchers = "" 472 | statusRebuilders = "" 473 | 474 | if isinstance(worker, CscopeSublimeSearchWorker): 475 | statusSearchers = "Fetching lookup results. " 476 | 477 | if isinstance(worker, CscopeSublimeDatabaseRebuildWorker): 478 | statusRebuilders = "Rebuilding cross-reference database. " 479 | 480 | self.view.set_status("CscopeSublime", 481 | "Cscope: {}{}[{}={}]" 482 | .format(statusSearchers, statusRebuilders, 483 | ' ' * count, ' ' * (7 - count))) 484 | sublime.set_timeout(lambda: self.update_status(count, dir), 100) 485 | break 486 | 487 | if not workInProgress: 488 | self.view.erase_status("CscopeSublime") 489 | output = "" 490 | for worker in self.workers: 491 | if isinstance(worker, CscopeSublimeSearchWorker): 492 | self.display_results(worker.symbol, worker.output) 493 | 494 | self.workers = [] 495 | 496 | def display_results(self, symbol, output): 497 | cscope_view = self.view.window().new_file() 498 | cscope_view.set_scratch(True) 499 | cscope_view.set_name("Cscope results - " + symbol) 500 | CscopeCommand.cscope_output_info['view'] = cscope_view 501 | CscopeCommand.cscope_output_info['pos'] = 0 502 | CscopeCommand.cscope_output_info['text'] = output 503 | CscopeCommand.cscope_output_info['symbol'] = symbol 504 | 505 | cscope_view.run_command("display_cscope_results") 506 | 507 | cscope_view.set_syntax_file(CSCOPE_SYNTAX_FILE) 508 | cscope_view.set_read_only(True) 509 | 510 | def run(self, edit, mode): 511 | self.mode = mode 512 | self.executable = get_setting("executable", "cscope") 513 | 514 | # Create a new database object every time we run. This way, if our user 515 | # deleted cscope files or recreated them, we have a correct understanding 516 | # of the current state. 517 | self.database = CscopeDatabase(view = self.view, 518 | executable = self.executable) 519 | self.database.update_location(self.view.file_name()) 520 | 521 | if mode == CSCOPE_CMD_DATABASE_REBUILD: 522 | self.rebuild_database() 523 | return 524 | 525 | if self.database.location == None: 526 | sublime.error_message("Could not find cscope database: cscope.out") 527 | return 528 | 529 | cur_pos = getCurrentPosition(self.view) 530 | if cur_pos: 531 | CscopeCommand.add_to_history(cur_pos) 532 | 533 | # Search for the first word that is selected. While Sublime Text uses 534 | # multiple selections, we only want the first selection since simultaneous 535 | # multiple cscope lookups don't make sense. 536 | first_selection = self.view.sel()[0] 537 | one = first_selection.a 538 | two = first_selection.b 539 | 540 | self.view.sel().add(sublime.Region(one, two)) 541 | 542 | symbol = self.view.substr(self.view.word(first_selection)) 543 | if get_setting("prompt_before_searching") == True: 544 | sublime.active_window().show_input_panel('Search Cscope for ' + CSCOPE_SEARCH_MODES[self.mode] + ':', 545 | symbol, 546 | self.on_search_confirmed, 547 | None, 548 | None) 549 | else: 550 | self.on_search_confirmed(symbol) 551 | 552 | def on_search_confirmed(self, symbol): 553 | worker = CscopeSublimeSearchWorker( 554 | view = self.view, 555 | platform = sublime.platform(), 556 | database = self.database, 557 | symbol = symbol, 558 | mode = self.mode, 559 | executable = self.executable 560 | ) 561 | worker.start() 562 | self.workers.append(worker) 563 | self.update_status() 564 | 565 | def rebuild_database(self): 566 | worker = CscopeSublimeDatabaseRebuildWorker(self.database) 567 | worker.start() 568 | self.workers.append(worker) 569 | self.update_status() 570 | 571 | 572 | class DisplayCscopeResultsCommand(sublime_plugin.TextCommand): 573 | 574 | def run(self, edit): 575 | self.view.insert(edit, CscopeCommand.cscope_output_info['pos'], CscopeCommand.cscope_output_info['text']) 576 | if get_setting("display_outline") == True: 577 | symbol_regions = self.view.find_all(CscopeCommand.cscope_output_info['symbol'], sublime.LITERAL) 578 | self.view.add_regions('cscopesublime-outlines', symbol_regions[1:], "text.find-in-files", "", sublime.DRAW_OUTLINED) 579 | --------------------------------------------------------------------------------