├── .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 | 
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 |
--------------------------------------------------------------------------------