aboutsummaryrefslogtreecommitdiffhomepage
path: root/core/ext/pm/ctags_browser.lua
diff options
context:
space:
mode:
authorGravatar mitchell <70453897+667e-11@users.noreply.github.com>2007-08-06 05:00:21 -0400
committerGravatar mitchell <70453897+667e-11@users.noreply.github.com>2007-08-06 05:00:21 -0400
commit538767b22d4641ffe4c6c5d5d82445c9e036bab6 (patch)
tree7187d27ef2d85a902a4eb9f92ab81a893816bbf7 /core/ext/pm/ctags_browser.lua
parentf23283b23db8dd67ac7c951ab4093b8b1d54ada4 (diff)
Initial import of extension Lua files.
Diffstat (limited to 'core/ext/pm/ctags_browser.lua')
-rw-r--r--core/ext/pm/ctags_browser.lua246
1 files changed, 246 insertions, 0 deletions
diff --git a/core/ext/pm/ctags_browser.lua b/core/ext/pm/ctags_browser.lua
new file mode 100644
index 00000000..e5607e7a
--- /dev/null
+++ b/core/ext/pm/ctags_browser.lua
@@ -0,0 +1,246 @@
+-- Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+---
+-- CTags Browser for the Textadept project manager.
+-- It is enabled with the prefix 'ctags' in the project manager entry field
+-- followed by either nothing or ':' and the path to a ctags file. If no path
+-- is specified, the current file is parsed via ctags and its structure shown.
+module('textadept.pm.browsers.ctags', package.seeall)
+
+local FILE_OUT = '/tmp/textadept_output'
+
+---
+-- The current ctags file and current directory.
+-- When a ctags file is opened, current_dir is set to its dirname.
+local current_file, current_dir
+
+---
+-- The table of ctags with property values.
+-- Each key is the name of a ctags identifier (function, class, etc.) and the
+-- value is a table containing:
+-- * The GTK stock-id for the pixbuf to display next to the identifier in the
+-- tree view (pixbuf key).
+-- * The display text used for displaying the identifier in the tree view
+-- (display_text key).
+-- * Boolean parent value if the identifier is a container.
+-- * The line number or pattern used to goto the identifier.
+-- Note this table is returned by get_contents_for, but only 'pixbuf',
+-- 'display_text' and 'parent' fields are read; all others are ignored.
+-- @class table
+-- @name tags
+local tags
+
+---
+-- Table of associations of tag identifier types for specific languages with
+-- GTK stock-id pixbufs.
+-- @class table
+-- @name pixbuf
+local pixbuf = {
+ lua = { f = 'prog-method' },
+ ruby = {
+ c = 'prog-class',
+ f = 'prog-method',
+ F = 'prog-method',
+ m = 'prog-namespace'
+ },
+ cpp = {
+ c = 'prog-class',
+ e = 'prog-enum',
+ f = 'prog-method',
+ g = 'prog-enum',
+ m = 'prog-field',
+ n = 'prog-namespace',
+ s = 'prog-struct'
+ }
+}
+
+---
+-- Table of associations of file extensions with languages.
+-- @class table
+-- @name language
+local language = {
+ lua = 'lua',
+ rb = 'ruby',
+ h = 'cpp', c = 'cpp', cxx = 'cpp' -- C++
+}
+
+---
+-- Table used to determine if a tag kind is a container or not in a specific
+-- language.
+-- Top-level keys are language names from the languages table with table
+-- values. These table values have tag kind keys with boolean values indicating
+-- if they are containers or not.
+-- @class table
+-- @name container
+-- @return true if the tag kind is a container.
+-- @see language
+local container = {
+ lua = {},
+ ruby = { c = true, m = true },
+ cpp = { c = true, g = true, s = true }
+}
+
+---
+-- Table used to determine if a construct name is a container or not in a
+-- specific language.
+-- Top-level keys are language names from the languages table with table
+-- values. These table values have construct name keys with boolean values
+-- indicating if they are containers or not.
+-- @class table
+-- @name container_construct
+-- @return true if the construct name is a container.
+-- @see language
+local container_construct = {
+ lua = {},
+ ruby = { class = true, module = true },
+ cpp = { class = true, enum = true, struct = true }
+}
+
+--- Matches 'ctags:[/absolute/path/to/ctags/file]'
+function matches(entry_text)
+ return entry_text:sub(1, 5) == 'ctags' and true or false
+end
+
+---
+-- If not expanding, create the entire tree; otherwise return the child table
+-- of the parent being expanded.
+function get_contents_for(full_path, expanding)
+ local ctags_file = full_path[1]:sub(7) -- ignore 'ctags:'
+ local f
+ if #ctags_file == 0 then
+ tags = {}
+ current_file = nil
+ current_dir = '' -- ctags file will specify absolute paths
+ os.execute( 'ctags -o '..FILE_OUT..' '..(buffer.filename or '') )
+ f = io.open(FILE_OUT)
+ if not f then return {} end
+ elseif not expanding then
+ tags = {}
+ current_file = ctags_file
+ current_dir = ctags_file:match('^.+/') -- ctags file dirname
+ f = io.open(ctags_file)
+ if not f then return {} end
+ else
+ local parent = tags
+ for i = 2, #full_path do
+ local identifier = full_path[i]
+ if not parent[identifier] then return {} end
+ parent = parent[identifier].children
+ end
+ return parent
+ end
+ for line in f:lines() do
+ if line:sub(1, 2) ~= '!_' then
+ -- Parse ctags line to get identifier attributes.
+ local name, filepath, pattern, line_num, ext
+ name, filepath, pattern, ext =
+ line:match('^([^\t]+)\t([^\t]+)\t/^(.+)$/;"\t(.*)$')
+ if not name then
+ name, filepath, line_num, ext =
+ line:match('^([^\t]+)\t([^\t]+)\t(%d+);"\t(.*)$')
+ end
+ -- If the ctag line is parsed correctly, create the entry.
+ if name and #name > 0 then
+ local entry = {}
+ local file_ext = filepath:match('%.([^.]+)$')
+ local lang = language[file_ext]
+ if lang then
+ -- Parse the extension fields for details on if this identifier is a
+ -- child or parent and where to put it.
+ local fields = {}
+ --print(ext)
+ for key, val in ext:gmatch('([^:%s]+):?(%S*)') do
+ if #val == 0 and #key == 1 then -- kind
+ if container[lang][key] then
+ -- This identifier is a container. Place it in the toplevel of
+ -- tags.
+ entry.parent = true
+ entry.children = {}
+ if tags[name] then
+ -- If previously defined by a child, preserve the children
+ -- field.
+ entry.children = tags[name].children
+ end
+ tags[name] = entry
+ entry.set = true
+ end
+ entry.pixbuf = pixbuf[lang][key]
+ elseif container_construct[lang][key] then
+ -- This identifier belongs to a container, so define the
+ -- container if it hasn't been already and place this identifier
+ -- in it. Just in case there is no ctag entry for container later
+ -- on, define 'parent' and 'display_text'.
+ if not tags[val] then
+ tags[val] = { parent = true, display_text = val }
+ end
+ local parent = tags[val]
+ if not parent.children then parent.children = {} end
+ parent.children[name] = entry -- add to parent
+ entry.set = true
+ end
+ end
+ entry.display_text = name
+ -- The following keys are ignored by caller.
+ entry.filepath = filepath:sub(1, 1) == '/' and
+ filepath or current_dir..filepath
+ entry.pattern = pattern
+ entry.line_num = line_num
+ if not entry.set then tags[name] = entry end
+ else
+ print('Extension "'..file_ext..'" not recognized.')
+ end
+ else print('unmatched ctag: '..line) end
+ end
+ end
+ f:close()
+ return tags
+end
+
+function perform_action(selected_item)
+ local item = tags
+ for i = 2, #selected_item do
+ local identifier = selected_item[i]
+ item = item[identifier]
+ if item.children then item = item.children end
+ end
+ if item.pattern then
+ local buffer_text = buffer:get_text(buffer.length)
+ local search_text = item.pattern:gsub('\\/', '/')
+ local s = buffer_text:find(search_text, 1, true)
+ if s then
+ textadept.io.open(item.filepath)
+ local line = buffer:line_from_position(s)
+ buffer:ensure_visible_enforce_policy(line)
+ buffer:goto_line(line)
+ else
+ error(item.display_text..' not found.')
+ end
+ elseif item.line_num then
+ textadept.io.open(item.filepath)
+ buffer:goto_line(item.line_num - 1)
+ end
+ view:focus()
+end
+
+function get_context_menu(selected_item)
+
+end
+
+function perform_menu_action(menu_item, selected_item)
+
+end
+
+local add_function_to_handler = textadept.handlers.add_function_to_handler
+local function update_view()
+ if matches(textadept.pm.entry_text) then
+ if buffer.filename then
+ textadept.pm.activate()
+ else
+ textadept.pm.clear()
+ end
+ end
+end
+add_function_to_handler('file_opened', update_view)
+add_function_to_handler('buffer_deleted', update_view)
+add_function_to_handler('buffer_switch', update_view)
+add_function_to_handler('save_point_reached', update_view)