├── Support ├── bin │ ├── ctags │ └── cscope └── graffiti.rb ├── Commands ├── Jump back.tmCommand ├── Complete.tmCommand ├── Find definitions of current symbol.tmCommand ├── Find files including current file.tmCommand ├── Jump to Locations of Current Symbol.tmCommand ├── Jump to file including current file.tmCommand ├── Update tags.tmCommand ├── Jump to function calling current function.tmCommand ├── Find Locations of a Symbol___.tmCommand ├── Find Definitions of a Symbol___.tmCommand ├── Find locations of current symbol.tmCommand ├── Jump to definition of current symbol.tmCommand ├── Find functions calling current function.tmCommand └── Help.tmCommand ├── README.mdown └── info.plist /Support/bin/ctags: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/textmate/graffiti.tmbundle/master/Support/bin/ctags -------------------------------------------------------------------------------- /Support/bin/cscope: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/textmate/graffiti.tmbundle/master/Support/bin/cscope -------------------------------------------------------------------------------- /Commands/Jump back.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | jumpBack() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | keyEquivalent 23 | ^x 24 | name 25 | Jump Back 26 | output 27 | discard 28 | uuid 29 | D3ABB124-274B-4614-8424-BD245BACFE3C 30 | 31 | 32 | -------------------------------------------------------------------------------- /Commands/Complete.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | completeCurrentWord() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | keyEquivalent 23 | ~ 24 | name 25 | Complete 26 | output 27 | discard 28 | uuid 29 | 38FABD8D-4FEB-4D5D-BD76-6D1159C5BD9A 30 | 31 | 32 | -------------------------------------------------------------------------------- /Commands/Find definitions of current symbol.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | findDefinitionsOfCurrentWord() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | name 23 | Find Definitions of Current Symbol 24 | output 25 | discard 26 | uuid 27 | E7A55A99-65D7-43F9-A35F-E4CEF02382EC 28 | 29 | 30 | -------------------------------------------------------------------------------- /Commands/Find files including current file.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | findFilesIncludingCurrentFile() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | name 23 | Find Files Including Current File 24 | output 25 | discard 26 | uuid 27 | 8AE754E9-FA13-4C34-969E-6872E30E70B9 28 | 29 | 30 | -------------------------------------------------------------------------------- /Commands/Jump to Locations of Current Symbol.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | jumpToLocationOfCurrentSymbol() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | name 23 | Jump to Location of Current Symbol 24 | output 25 | discard 26 | uuid 27 | 2CE92A0D-1D44-4D4B-9304-02E13449E79F 28 | 29 | 30 | -------------------------------------------------------------------------------- /Commands/Jump to file including current file.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | jumpToFileIncludingCurrentFile() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | name 23 | Jump to File Including Current File 24 | output 25 | discard 26 | uuid 27 | 9F156671-94A4-4BDB-AC7A-2EDA8984414D 28 | 29 | 30 | -------------------------------------------------------------------------------- /Commands/Update tags.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | saveModifiedFiles 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | updateTagFiles() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | keyEquivalent 23 | ^f 24 | name 25 | Update Tags 26 | output 27 | showAsHTML 28 | uuid 29 | 5059ADFA-F217-4BA0-A92D-2001D6465F7F 30 | 31 | 32 | -------------------------------------------------------------------------------- /Commands/Jump to function calling current function.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | jumpToFunctionCallingCurrentFunction() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | name 23 | Jump to Function Calling Current Function 24 | output 25 | discard 26 | uuid 27 | 1021D5F2-36D1-4C1E-BBA0-A47F6A020F58 28 | 29 | 30 | -------------------------------------------------------------------------------- /Commands/Find Locations of a Symbol___.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | findLocationsOfPromptedWord() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | keyEquivalent 23 | ^f 24 | name 25 | Find Locations of a Symbol... 26 | output 27 | discard 28 | uuid 29 | 97CF2784-6770-4CF8-A74E-5E5456808251 30 | 31 | 32 | -------------------------------------------------------------------------------- /Commands/Find Definitions of a Symbol___.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | findDefinitionsOfPromptedWord() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | keyEquivalent 23 | ^f 24 | name 25 | Find Definitions of a Symbol... 26 | output 27 | discard 28 | uuid 29 | EA53FDBC-913F-4591-AADE-3E94A81289C9 30 | 31 | 32 | -------------------------------------------------------------------------------- /Commands/Find locations of current symbol.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | findLocationsOfCurrentSymbol() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | keyEquivalent 23 | ^f 24 | name 25 | Find Locations of Current Symbol 26 | output 27 | discard 28 | uuid 29 | 946326A3-8BE5-4C0C-8147-9BF6625D4B75 30 | 31 | 32 | -------------------------------------------------------------------------------- /Commands/Jump to definition of current symbol.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | jumpToDefinitionOfCurrentWord() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | keyEquivalent 23 | ^c 24 | name 25 | Jump to Definition of Current Symbol 26 | output 27 | discard 28 | uuid 29 | 7AEB2540-DB8A-4B77-B499-34BE8285B95A 30 | 31 | 32 | -------------------------------------------------------------------------------- /Commands/Find functions calling current function.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | autoScrollOutput 6 | 7 | beforeRunningCommand 8 | nop 9 | command 10 | #!/usr/bin/env ruby 11 | 12 | require ENV['TM_BUNDLE_SUPPORT'] + '/graffiti.rb' 13 | 14 | include Graffiti 15 | 16 | findFunctionsCallingCurrentFunction() 17 | 18 | fallbackInput 19 | line 20 | input 21 | none 22 | keyEquivalent 23 | ^f 24 | name 25 | Find Functions Calling Current Function 26 | output 27 | discard 28 | uuid 29 | C51C2C36-3772-46A9-B1EC-29CD380F5C80 30 | 31 | 32 | -------------------------------------------------------------------------------- /README.mdown: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | You can install this bundle in TextMate by opening the preferences and going to the bundles tab. After installation it will be automatically updated for you. 4 | 5 | # General 6 | 7 | * [Bundle Styleguide](http://kb.textmate.org/bundle_styleguide) — _before you make changes_ 8 | * [Commit Styleguide](http://kb.textmate.org/commit_styleguide) — _before you send a pull request_ 9 | * [Writing Bug Reports](http://kb.textmate.org/writing_bug_reports) — _before you report an issue_ 10 | 11 | # License 12 | 13 | If not otherwise specified (see below), files in this repository fall under the following license: 14 | 15 | Permission to copy, use, modify, sell and distribute this 16 | software is granted. This software is provided "as is" without 17 | express or implied warranty, and with no claim as to its 18 | suitability for any purpose. 19 | 20 | An exception is made for files in readable text which contain their own license information, or files where an accompanying file exists (in the same directory) with a “-license” suffix added to the base-name name of the original file, and an extension of txt, html, or similar. For example “tidy” is accompanied by “tidy-license.txt”. -------------------------------------------------------------------------------- /info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | contactEmailRot13 6 | znguvrh.tbqneg@tznvy.pbz 7 | contactName 8 | Mathieu Godart 9 | description 10 | The Graffiti TextMate bundle provides a set of commands to manipulate and browse Ruby, C/C++ and Java source code. 11 | mainMenu 12 | 13 | items 14 | 15 | 38FABD8D-4FEB-4D5D-BD76-6D1159C5BD9A 16 | ------------------------------------ 17 | 7AEB2540-DB8A-4B77-B499-34BE8285B95A 18 | ------------------------------------ 19 | D3ABB124-274B-4614-8424-BD245BACFE3C 20 | ------------------------------------ 21 | 9F156671-94A4-4BDB-AC7A-2EDA8984414D 22 | 2CE92A0D-1D44-4D4B-9304-02E13449E79F 23 | 1021D5F2-36D1-4C1E-BBA0-A47F6A020F58 24 | ------------------------------------ 25 | E7A55A99-65D7-43F9-A35F-E4CEF02382EC 26 | 8AE754E9-FA13-4C34-969E-6872E30E70B9 27 | 946326A3-8BE5-4C0C-8147-9BF6625D4B75 28 | C51C2C36-3772-46A9-B1EC-29CD380F5C80 29 | ------------------------------------ 30 | 97CF2784-6770-4CF8-A74E-5E5456808251 31 | EA53FDBC-913F-4591-AADE-3E94A81289C9 32 | ------------------------------------ 33 | 5059ADFA-F217-4BA0-A92D-2001D6465F7F 34 | ------------------------------------ 35 | 04CD64C3-29E2-428B-BD66-6B5756740485 36 | 37 | submenus 38 | 39 | 40 | name 41 | Graffiti 42 | ordering 43 | 44 | 38FABD8D-4FEB-4D5D-BD76-6D1159C5BD9A 45 | 7AEB2540-DB8A-4B77-B499-34BE8285B95A 46 | D3ABB124-274B-4614-8424-BD245BACFE3C 47 | 2CE92A0D-1D44-4D4B-9304-02E13449E79F 48 | 1021D5F2-36D1-4C1E-BBA0-A47F6A020F58 49 | 9F156671-94A4-4BDB-AC7A-2EDA8984414D 50 | E7A55A99-65D7-43F9-A35F-E4CEF02382EC 51 | 8AE754E9-FA13-4C34-969E-6872E30E70B9 52 | 946326A3-8BE5-4C0C-8147-9BF6625D4B75 53 | C51C2C36-3772-46A9-B1EC-29CD380F5C80 54 | 97CF2784-6770-4CF8-A74E-5E5456808251 55 | EA53FDBC-913F-4591-AADE-3E94A81289C9 56 | 5059ADFA-F217-4BA0-A92D-2001D6465F7F 57 | 04CD64C3-29E2-428B-BD66-6B5756740485 58 | 59 | uuid 60 | 4477B373-822D-4298-806B-1EF13DBA4FD9 61 | 62 | 63 | -------------------------------------------------------------------------------- /Commands/Help.tmCommand: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | beforeRunningCommand 6 | nop 7 | command 8 | . "$TM_SUPPORT_PATH/lib/webpreview.sh" 9 | html_header "Graffiti Help" "Graffiti" 10 | 11 | "$TM_SUPPORT_PATH/lib/markdown_to_help.rb" <<'EOF' 12 | 13 | # Introduction 14 | 15 | The Graffiti TextMate bundle provides a set of commands to manipulate and browse source code in Ruby, Makefiles, C/C++, Java, etc. It is based on two commands famous in the Unix world: [CScope][1] and [Exuberant CTags][2]. 16 | 17 | For example, it allows you to jump to the definition of a function and then to jump back, it finds all the locations where a symbol can be found, it provides a completion menu made up of all the class, function and variable names found in the code of your project... 18 | 19 | See the *Command List* section for more information on what Graffiti can do for you. 20 | 21 | # First Steps 22 | 23 | Before being able to use any other command of this bundle, you must create the tags, an index of the source code (for each one of your projects). 24 | 25 | To do so, open your project (Graffiti does not work on single documents), call the command **Update tags**. This may take some time on very large projects. 26 | 27 | Graffiti will create an index, recursively, for the source files found in the directory `TM_PROJECT_DIRECTORY`. If you have trouble with this command, check that your environment variable `TM_PROJECT_DIRECTORY` is well defined. This variable can be overwritten in the [Project Information][3] window. 28 | 29 | # Command List 30 | 31 | **Complete** - 32 | Use the `ctags` file to display a list of completion proposals as a popup. 33 | 34 | **Jump to Definition of Current Symbol** - 35 | Ask to `ctags` the locations where the symbol can be found, 36 | ask the user via popup to choose one and jump to it at the proper line. 37 | 38 | **Jump Back** - 39 | Jump to the previous location in the history. 40 | 41 | **Jump to Function Calling Current Function** *(only for C/C++ and Java)* - 42 | Ask to `cscope` where the function under the cursor is called, 43 | ask the user via popup to choose one and jump to it at the proper line. 44 | 45 | **Jump to File Including Current File** *(only for C/C++ and Java)* - 46 | Ask to `cscope` where the current file is #included, 47 | ask the user via popup to choose one and jump to it at the proper line. 48 | 49 | **Find Definitions of Current Symbol** - 50 | Ask to `ctags` where the symbol is defined, 51 | and print it out as a nice HTML list. 52 | 53 | **Find Locations of Current Symbol** *(only for C/C++ and Java)* - 54 | Ask to `cscope` the locations where the symbol can be found, 55 | and print it out as a nice HTML list. 56 | 57 | **Find Functions Calling Current Function** *(only for C/C++ and Java)* - 58 | Ask to `cscope` where the function under the cursor is called, 59 | and print it out as a nice HTML list. 60 | 61 | **Find Files Including Current File** *(only for C/C++ and Java)* - 62 | Ask to `cscope` where the current file is #included, 63 | and print it out as a nice HTML list. 64 | 65 | **Find Definitions of a Symbol...** - 66 | Display a prompt so that the user can enter a symbol, 67 | ask to `cscope` where this symbol is defined, 68 | and print it out as a nice HTML list. 69 | 70 | **Find Locations of a Symbol...** *(only for C/C++ and Java)* - 71 | Display a prompt so that the user can enter a symbol or a regular expression, 72 | ask to `ctags` the locations where this symbol can be found, 73 | and print it out as a nice HTML list. 74 | 75 | **Update Tags** - 76 | Create the `.graffiti` if needed. Create `ctags` and `cscope` tag files 77 | starting from `TM_PROJECT_DIRECTORY`. Display this in a nice HTML output. 78 | 79 | # Configuration 80 | 81 | `TM_PROJECT_DIRECTORY` - 82 | Graffiti create its tags index by recursively looking for all the source code files from the `TM_PROJECT_DIRECTORY` directory. Note that the path to the source files shall not contain spaces (`cscope` seems to have trouble with the space character). 83 | 84 | `.graffiti` - 85 | All the project related files (tag files, history, etc.) are located in this directory. It is created by Graffiti here: `TM_PROJECT_DIRECTORY/.graffiti`. 86 | 87 | # Maintainer 88 | 89 | For questions or sugestions, please contact [Mathieu Godart][4]. 90 | 91 | [1]: http://ctags.sourceforge.net 92 | [2]: http://cscope.sourceforge.net/cscope_man_page.html 93 | [3]: http://manual.macromates.com/en/environment_variables#project_dependent_variables 94 | [4]: mathieu.godart@gmail.com 95 | 96 | EOF 97 | fallbackInput 98 | line 99 | input 100 | none 101 | name 102 | Help 103 | output 104 | showAsHTML 105 | uuid 106 | 04CD64C3-29E2-428B-BD66-6B5756740485 107 | 108 | 109 | -------------------------------------------------------------------------------- /Support/graffiti.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | 4 | ############################################################################### 5 | # Test cases. 6 | # hal_Open 7 | # memd_FlashOpen 8 | # HAL_ASSERT 9 | # HAL_CFG_CONFIG_T 10 | # 11 | 12 | 13 | ############################################################################### 14 | # The tools `ctags` and `cscope` are in the directory ('$TM_BUNDLE_SUPPORT/bin'). 15 | # 16 | require ENV['TM_SUPPORT_PATH'] + '/lib/web_preview.rb' 17 | require ENV['TM_SUPPORT_PATH'] + '/lib/textmate.rb' 18 | require ENV['TM_SUPPORT_PATH'] + '/lib/ui.rb' 19 | require 'yaml' 20 | 21 | 22 | ############################################################################### 23 | # Module Graffiti, containing all the methods and data members of the bundle. 24 | # 25 | module Graffiti 26 | 27 | 28 | ############################################################################### 29 | # Module data members. 30 | # 31 | # Define the locations of the tag and history files. 32 | # 33 | # TODO: check also the TM_PROJECT_FILEPATH variable. 34 | # 35 | @@bundleName = "Graffiti" 36 | @@ctags = "\"#{ENV['TM_BUNDLE_SUPPORT']}/bin/ctags\"" 37 | @@cscope = "\"#{ENV['TM_BUNDLE_SUPPORT']}/bin/cscope\"" 38 | @@projectDirPath = ENV['TM_PROJECT_DIRECTORY'] 39 | @@prefDirName = '.graffiti' 40 | @@prefDirPath = @@projectDirPath.to_s + "/" + @@prefDirName 41 | 42 | @@ctagsTagFileName = 'ctags.tags' 43 | @@ctagsTagFilePath = @@prefDirPath + "/" + @@ctagsTagFileName 44 | @@ctagsTagFileShortPath = @@prefDirName + "/" + @@ctagsTagFileName 45 | @@ctagsIdentifiers = "VOID=void" 46 | 47 | @@cscopeTagFileName = 'cscope.out' 48 | @@cscopeTagFilePath = @@prefDirPath + '/' + @@cscopeTagFileName 49 | @@cscopeTagFileShortPath = @@prefDirName + '/' + @@cscopeTagFileName 50 | 51 | @@cscopeDbFileName = 'cscope.files' 52 | @@cscopeDbFilePath = @@prefDirPath + '/' + @@cscopeDbFileName 53 | @@cscopeDbFileShortPath = @@prefDirName + '/' + @@cscopeDbFileName 54 | 55 | @@historyFilePath = @@prefDirPath + '/history.yaml' 56 | @@debugFilePath = @@prefDirPath + '/debug.log' 57 | 58 | 59 | ############################################################################### 60 | # askCtags 61 | # 62 | # Query `ctags` with a 'symbol' name and return the array 'tagsTable' with 63 | # the list of results. 64 | # This is very similar to 'askCscope' but supports more languages. 65 | # 66 | # 'tagsTable' contains one line per result found. This line contains: 67 | # 0 - File path. 68 | # 1 - Symbol name. 69 | # 2 - Line number. 70 | # 3 - Text of the line where the definition has been found. 71 | # 4 - Tag type. 72 | # 5 - Language type. 73 | # 6 - Signature. 74 | # 75 | # The returned table cannot be null, but can be empty. 76 | # 77 | def askCtags(symbol) 78 | checkTagFiles() 79 | 80 | tagsTable = [] 81 | ctagsAskCmd = "grep '^#{symbol}\t.*' #{@@ctagsTagFilePath} | sort -u" 82 | 83 | debugPrint ctagsAskCmd #DEBUG 84 | ctagsCmdResult = `#{ctagsAskCmd}` 85 | ctagsCmdResult.each_line do |line| 86 | lineArray = line.chop.split("\t") 87 | tagsTable << [@@projectDirPath.to_s + "/" + lineArray[1], 88 | lineArray[0], 89 | lineArray[2].to_s.gsub(';"', ''), 90 | lineArray[0] + lineArray[5].to_s.gsub('signature:', ''), 91 | lineArray[3].to_s.gsub('kind:', ''), 92 | lineArray[4].to_s.gsub('language:', ''), 93 | lineArray[5].to_s.gsub('signature:', '')].flatten 94 | end 95 | 96 | return tagsTable 97 | end 98 | 99 | 100 | ############################################################################### 101 | # askCscope 102 | # 103 | # Query `cscope` with a 'symbol' name and a 'cmdId' command number and build 104 | # and return the array 'tagsTable' with query result. 105 | # 106 | # 'tagsTable' contains one line per result found. This line contains: 107 | # 0 - File path. 108 | # 1 - Symbol name. 109 | # 2 - Line number. 110 | # 3 - Text of the line where the definition has been found. 111 | # 112 | # The returned table cannot be null, but can be empty. 113 | # 114 | def askCscope(cmdId, symbol) 115 | checkTagFiles() 116 | 117 | tagsTable = [] 118 | # -d Do not update the cross-reference. 119 | # -L Do a single search with line-oriented output. 120 | # -q Enable fast symbol lookup via an inverted index. 121 | cscopeAskCmd = "cd '#{@@projectDirPath}' && " + 122 | "#{@@cscope} -f #{@@cscopeTagFileShortPath} -L -d -q -#{cmdId}\"#{symbol}\"" 123 | debugPrint cscopeAskCmd #DEBUG 124 | cscopeCmdResult = `#{cscopeAskCmd}` 125 | cscopeCmdResult.each_line do |line| 126 | lineArray = line.chop.split(" ") 127 | tagsTable << [@@projectDirPath.to_s + "/" + lineArray[0], 128 | lineArray[1..2], 129 | lineArray[3..-1].join(" ")].flatten 130 | end 131 | 132 | return tagsTable 133 | end 134 | 135 | 136 | ############################################################################### 137 | # askToolPopupResults 138 | # 139 | # If 'cmdId' is in 0..8, query `cscope` with a 'symbol' name and a 'cmdId' 140 | # command number, if 'cmdId' is :ctags, query `ctags`. Then ask the user to 141 | # choose one, with a popup menu, if multiple occurrences are found. 142 | # 143 | # Return 'tagsTable' contains one line. This line contains: 144 | # 0 - File path. 145 | # 1 - Symbol name. 146 | # 2 - Line number. 147 | # 3 - Text of the line where the definition has been found. 148 | # 149 | # The returned table cannot be null and cannot be empty. If nothing found 150 | # or selected by user, show a tool tip and exit. 151 | # 152 | def askToolPopupResults(cmdId, symbol) 153 | # Ask to `cscope` the list of locations. 154 | tagsTable = case cmdId 155 | when :ctags then askCtags(symbol) 156 | when 0..8 then askCscope(cmdId, symbol) 157 | end 158 | 159 | if (tagsTable.size == 0) then 160 | # No match. 161 | raise "Nothing found." 162 | elsif (tagsTable.size == 1) 163 | # Only one match. 164 | selected = 0 165 | else 166 | # Multiple occurrences, display a selection menu. 167 | # Build the array 'tagsMenu' containing only the paths. 168 | tagsMenu = [] 169 | tagsTable.each do |tagsTableLine| 170 | tagsMenu << tagsTableLine[0].sub(@@projectDirPath, '') 171 | end 172 | selected = TextMate::UI.menu(tagsMenu) 173 | # Esc pressed. 174 | exit if (selected == nil) 175 | end 176 | 177 | # Return the selected file. 178 | return tagsTable[selected] 179 | end 180 | 181 | 182 | ############################################################################### 183 | # displayTagsTable 184 | # 185 | # Get a tags table returned by ask `cscope` function and display it as a list 186 | # in a nice HTML output. 187 | # 188 | def displayTagsTable(title, tagsTable) 189 | raise "Nothing found." if tagsTable.empty? 190 | 191 | # Sort the table according to the file name and line number. 192 | tagsTable.sort! do |x, y| 193 | order = (x[0] <=> y[0]) 194 | if order == 0 then 195 | (x[2].to_i <=> y[2].to_i) 196 | else 197 | order 198 | end 199 | end 200 | 201 | html_header(title, @@bundleName) 202 | print "\n" 238 | 239 | print "Done. Found #{tagsTable.size} " + 240 | "instance#{"s" if (tagsTable.size > 1)}.

\n" 241 | 242 | html_footer 243 | 244 | TextMate.exit_show_html 245 | end 246 | 247 | 248 | ############################################################################### 249 | # findLocationsOfPromptedWord 250 | # 251 | # Display a prompt so that the user can enter a symbol or a regular expression, 252 | # ask to `cscope` the locations where this symbol can be found, 253 | # and print it out as a nice HTML list. 254 | # 255 | def findLocationsOfPromptedWord() 256 | promptedWord = askWordToUser() 257 | exit if promptedWord.nil? 258 | title = "Locations of \"#{promptedWord}\"" 259 | displayTagsTable(title, askCscope(0, promptedWord)) 260 | rescue => msg 261 | toolTip(msg) 262 | end 263 | 264 | 265 | ############################################################################### 266 | # findLocationsOfCurrentSymbol 267 | # 268 | # Ask to `cscope` the locations where the symbol can be found, 269 | # and print it out as a nice HTML list. 270 | # 271 | def findLocationsOfCurrentSymbol() 272 | title = "Locations of \"#{getCurrentWord()}\"" 273 | displayTagsTable(title, askCscope(0, getCurrentWord())) 274 | rescue => msg 275 | toolTip(msg) 276 | end 277 | 278 | 279 | ############################################################################### 280 | # jumpToLocationOfCurrentSymbol 281 | # 282 | # Ask to `cscope` the locations where the symbol can be found, 283 | # ask the user via popup to choose one and jump to it at the proper line. 284 | # 285 | def jumpToLocationOfCurrentSymbol() 286 | jumpToTagLine(askToolPopupResults(0, getCurrentWord())) 287 | rescue => msg 288 | toolTip(msg) 289 | end 290 | 291 | 292 | ############################################################################### 293 | # findDefinitionsOfPromptedWord 294 | # 295 | # Display a prompt so that the user can enter a symbol, 296 | # ask to `ctags` where this symbol is defined, 297 | # and print it out as a nice HTML list. 298 | # 299 | def findDefinitionsOfPromptedWord() 300 | promptedWord = askWordToUser() 301 | exit if promptedWord.nil? 302 | title = "Definitions of \"#{promptedWord}\"" 303 | displayTagsTable(title, askCtags(promptedWord)) 304 | rescue => msg 305 | toolTip(msg) 306 | end 307 | 308 | 309 | ############################################################################### 310 | # findDefinitionsOfCurrentWord 311 | # 312 | # Ask to `ctags` where the symbol is defined, 313 | # and print it out as a nice HTML list. 314 | # 315 | def findDefinitionsOfCurrentWord() 316 | title = "Definitions of \"#{getCurrentWord()}\"" 317 | displayTagsTable(title, askCtags(getCurrentWord())) 318 | rescue => msg 319 | toolTip(msg) 320 | end 321 | 322 | 323 | ############################################################################### 324 | # jumpToDefinitionOfCurrentWord 325 | # 326 | # Ask to `ctags` where the symbol is defined, 327 | # ask the user via popup to choose one and jump to it at the proper line. 328 | # 329 | def jumpToDefinitionOfCurrentWord() 330 | jumpToTagLine(askToolPopupResults(:ctags, getCurrentWord())) 331 | rescue => msg 332 | toolTip(msg) 333 | end 334 | 335 | 336 | ############################################################################### 337 | # findFunctionsCallingCurrentFunction 338 | # 339 | # Ask to `cscope` where the function under the cursor is called, 340 | # and print it out as a nice HTML list. 341 | # 342 | def findFunctionsCallingCurrentFunction() 343 | title = "Callers of \"#{getCurrentWord()}\"" 344 | displayTagsTable(title, askCscope(3, getCurrentWord())) 345 | rescue => msg 346 | toolTip(msg) 347 | end 348 | 349 | 350 | ############################################################################### 351 | # jumpToFunctionCallingCurrentFunction 352 | # 353 | # Ask to `cscope` where the function under the cursor is called, 354 | # ask the user via popup to choose one and jump to it at the proper line. 355 | # 356 | def jumpToFunctionCallingCurrentFunction() 357 | jumpToTagLine(askToolPopupResults(3, getCurrentWord())) 358 | rescue => msg 359 | toolTip(msg) 360 | end 361 | 362 | 363 | ############################################################################### 364 | # findFilesIncludingCurrentFile 365 | # 366 | # Ask to `cscope` where the current file is #included, 367 | # and print it out as a nice HTML list. 368 | # 369 | def findFilesIncludingCurrentFile() 370 | title = "Inclusions of \"#{getCurrentFileName()}\"" 371 | displayTagsTable(title, askCscope(8, getCurrentFileName())) 372 | rescue => msg 373 | toolTip(msg) 374 | end 375 | 376 | 377 | ############################################################################### 378 | # jumpToFileIncludingCurrentFile 379 | # 380 | # Ask to `cscope` where the current file is #included, 381 | # ask the user via popup to choose one and jump to it at the proper line. 382 | # 383 | def jumpToFileIncludingCurrentFile() 384 | jumpToTagLine(askToolPopupResults(8, getCurrentFileName())) 385 | rescue => msg 386 | toolTip(msg) 387 | end 388 | 389 | 390 | ############################################################################### 391 | # loadJumpHistory 392 | # 393 | # Return a table containing the previous locations. 394 | # This table can be empty but not null. 395 | # 396 | def loadJumpHistory() 397 | return [] unless FileTest.exist?(@@historyFilePath) 398 | history = File.open(@@historyFilePath) { |yf| YAML::load(yf) } 399 | return [] if history == false 400 | return history 401 | end 402 | 403 | 404 | ############################################################################### 405 | # saveJumpHistory 406 | # 407 | # Save a table containing the previous locations. 408 | # This table can be empty but not null. 409 | # 410 | def saveJumpHistory(history) 411 | File.open(@@historyFilePath, 'w') { |yf| YAML.dump(history, yf) } 412 | end 413 | 414 | 415 | ############################################################################### 416 | # jumpToTagLine 417 | # 418 | # Jump to a location returned by ask `cscope` functions. 419 | # 420 | def jumpToTagLine(tagLine) 421 | jumpToFile(tagLine[0], tagLine[2].to_i, 0) 422 | end 423 | 424 | 425 | ############################################################################### 426 | # jumpToFile 427 | # 428 | # Jump to 'file' at 'line' and 'column' and add the current location to 429 | # the history. 430 | # 431 | def jumpToFile(file, line, column) 432 | # Load the history array. 433 | history = loadJumpHistory() 434 | 435 | # Save the current location in the history array. 436 | history.push({:file => ENV['TM_FILEPATH'], :line => ENV['TM_LINE_NUMBER'].to_i, :column => ENV['TM_COLUMN_NUMBER'].to_i}) 437 | 438 | # Save the history array. 439 | saveJumpHistory(history) 440 | 441 | # Open the selected file at the right line and column. 442 | TextMate::go_to({:file => file, :line => line, :column => column}) 443 | end 444 | 445 | 446 | ############################################################################### 447 | # jumpBack 448 | # 449 | # Jump to the previous location in the history. 450 | # Delete this location from history. 451 | # 452 | def jumpBack() 453 | # Load the history array. 454 | history = loadJumpHistory() 455 | raise "History is empty." if history.empty? 456 | 457 | # Get the last location from history, remove this location from the 458 | # history and open this location. 459 | TextMate::go_to(history.pop) 460 | 461 | # Save the history array. 462 | saveJumpHistory(history) 463 | 464 | rescue => msg 465 | toolTip(msg) 466 | end 467 | 468 | 469 | ############################################################################### 470 | # completeCurrentWord 471 | # 472 | # Use the `ctags` file to display a list of completion proposals as a popup. 473 | # TODO: Find out why '_' is not handled as a normal char, as expected. 474 | # 475 | def completeCurrentWord() 476 | currentWord = getCurrentWord() 477 | checkTagFiles() 478 | 479 | # Build the list of completion choice. 480 | choices = [] 481 | grepCmd = "grep '^#{currentWord}' #{@@ctagsTagFilePath} | " + 482 | "sed -e 's/^\\(#{currentWord}[[:alnum:]_]*\\).*/\\1/p' -e 'd' | sort -u" 483 | `#{grepCmd}`.split("\n").each do |tagLine| 484 | # choices += [{'display' => tagLine, 'insert' => tagLine.sub(currentWord, '')}] 485 | # choices += [{'display' => tagLine, 'match' => tagLine.sub(currentWord, '')}] 486 | choices += [{'display' => tagLine}] 487 | end 488 | 489 | raise "Nothing found." if choices.size == 0 490 | 491 | # Display the completion popup. 492 | options = {:extra_chars => '_', :case_insensitive => false} 493 | TextMate::UI.complete(choices, options) 494 | 495 | rescue => msg 496 | toolTip(msg) 497 | end 498 | 499 | 500 | ############################################################################### 501 | # updateTagFiles 502 | # 503 | # Create the prefDirPath if needed. Create `ctags` and `cscope` tag files 504 | # starting from projectDirPath. Display this in a nice HTML output. 505 | # Check the existence of the project directory. 506 | # 507 | # TODO: Try the option "−L file" of `ctags`. 508 | # TODO: Try the option "−k" of `cscope`. 509 | # TODO: Add a way to exclude some directories. 510 | # TODO: Try to see if it is possible to just update the `cscope` database. 511 | # 512 | def updateTagFiles() 513 | html_header("Updating Tags", @@bundleName) 514 | 515 | # Check that the project directory is defined. 516 | unless (FileTest.directory?(@@projectDirPath.to_s)) then 517 | print "The environment variable TM_PROJECT_DIRECTORY is " + 518 | "not defined. For details, please see First Steps section of the " + 519 | "Graffiti Help command.

\n" 520 | return 521 | end 522 | 523 | if (@@projectDirPath.to_s[' '] != nil) then 524 | print "The environment variable TM_PROJECT_DIRECTORY contains " + 525 | "spaces, sorry but this is not supported by the cscope tool.

\n" 526 | return 527 | end 528 | 529 | print "Updating the tag files for #{@@projectDirPath}.

\n" 530 | print "
Update complete.\n" 573 | 574 | ensure 575 | html_footer 576 | end 577 | 578 | 579 | ############################################################################### 580 | # toolTip 581 | # 582 | # Show an HTML tooltip displaying 'msg'. 583 | # 584 | def toolTip(msg) 585 | TextMate::UI.tool_tip(msg, {:format => :html}) 586 | end 587 | 588 | 589 | ############################################################################### 590 | # checkTagFile 591 | # 592 | # Check that the `ctags` and `cscope` tag files are present. 593 | # 594 | # TODO: Add the check for `cscope`. 595 | # 596 | def checkTagFiles() 597 | raise "No tag file found, please run 'Update Tags' command." unless FileTest.exist?(@@ctagsTagFilePath) 598 | end 599 | 600 | 601 | ############################################################################### 602 | # getCurrentWord 603 | # 604 | # Get the word under the cursor, raise when nothing under the cursor. 605 | # 606 | def getCurrentWord() 607 | currentWord = ENV['TM_CURRENT_WORD'] 608 | raise "Nothing under the cursor." if currentWord.nil? 609 | return currentWord 610 | end 611 | 612 | 613 | ############################################################################### 614 | # askWordToUser 615 | # 616 | # Ask the user to provide a word. 617 | # 618 | # TODO: get the default value from the history. 619 | # 620 | def askWordToUser() 621 | options = {:title => 'Symbol to find', :prompt => 'Symbol to find:', :default => ''} 622 | word = TextMate::UI.request_string(options) 623 | return word 624 | end 625 | 626 | 627 | ############################################################################### 628 | # getCurrentFile 629 | # 630 | # Get the basename of the file currently opened, raise when empty (?). 631 | # 632 | def getCurrentFileName() 633 | currentFile = File.basename(ENV['TM_FILEPATH']) # TM_SELECTED_FILE ? 634 | raise "No current file." if currentFile.nil? 635 | return currentFile 636 | end 637 | 638 | 639 | ############################################################################### 640 | # debugPrint 641 | # 642 | # Print out the error message at a location accessible by the developer. 643 | # 644 | def debugPrint(msg) 645 | File.open(@@debugFilePath, "w+") do |file| 646 | file << msg 647 | end 648 | end 649 | 650 | 651 | end 652 | --------------------------------------------------------------------------------