aboutsummaryrefslogtreecommitdiffhomepage
path: root/core
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
parentf23283b23db8dd67ac7c951ab4093b8b1d54ada4 (diff)
Initial import of extension Lua files.
Diffstat (limited to 'core')
-rw-r--r--core/ext/find.lua107
-rw-r--r--core/ext/mime_types.lua86
-rw-r--r--core/ext/pm.lua95
-rw-r--r--core/ext/pm/buffer_browser.lua57
-rw-r--r--core/ext/pm/ctags_browser.lua246
-rw-r--r--core/ext/pm/file_browser.lua64
6 files changed, 655 insertions, 0 deletions
diff --git a/core/ext/find.lua b/core/ext/find.lua
new file mode 100644
index 00000000..bc33b3cd
--- /dev/null
+++ b/core/ext/find.lua
@@ -0,0 +1,107 @@
+-- Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+local find = textadept.find
+
+---
+-- [Local table] Text escape sequences with their associated characters.
+-- @class table
+-- @name escapes
+local escapes = {
+ ['\\a'] = '\a', ['\\b'] = '\b', ['\\f'] = '\f', ['\\n'] = '\n',
+ ['\\r'] = '\r', ['\\t'] = '\t', ['\\v'] = '\v', ['\\\\'] = '\\'
+}
+
+---
+-- Finds and selects text in the current buffer.
+-- This is used by the find dialog. It is recommended to use the buffer:find()
+-- function for scripting.
+-- @param text The text to find.
+-- @param flags Search flags. This is a number mask of 3 flags: match case (2),
+-- whole word (4), and Lua pattern (8) joined with binary AND.
+-- @param next Boolean indicating search direction (next is forward).
+-- @param wrapped Utility boolean indicating whether the search has wrapped or
+-- not for displaying useful statusbar information. This flag is used and set
+-- internally, and should not be set otherwise.
+function find.find(text, flags, next, wrapped)
+ local buffer = buffer
+ local result
+ text = text:gsub('\\[abfnrtv\\]', escapes)
+ find.captures = nil
+ if flags < 8 then
+ if next then
+ buffer:goto_pos(buffer.current_pos + 1)
+ buffer:search_anchor()
+ result = buffer:search_next(flags, text)
+ else
+ buffer:goto_pos(buffer.anchor - 1)
+ buffer:search_anchor()
+ result = buffer:search_prev(flags, text)
+ end
+ if result then buffer:scroll_caret() end
+ else -- lua pattern search
+ local buffer_text = buffer:get_text(buffer.length)
+ local results = { buffer_text:find(text, buffer.anchor + 1) }
+ if #results > 0 then
+ result = results[1]
+ find.captures = { unpack(results, 3) }
+ buffer:set_sel(results[2], result - 1)
+ else
+ result = -1
+ end
+ end
+ if result == -1 and not wrapped then -- wrap the search
+ local anchor, pos = buffer.anchor, buffer.current_pos
+ if next or flags >= 8 then
+ buffer:goto_pos(0)
+ else
+ buffer:goto_pos(buffer.length)
+ end
+ textadept.statusbar_text = 'Search wrapped'
+ result = find.find(text, flags, next, true)
+ if not result then
+ textadept.statusbar_text = 'No results found'
+ buffer:goto_pos(anchor)
+ end
+ return result
+ elseif result ~= -1 and not wrapped then
+ textadept.statusbar_text = ''
+ end
+ return result ~= -1
+end
+
+---
+-- Replaces found text.
+-- This function is used by the find dialog. It is not recommended to call it
+-- via scripts.
+-- textadept.find.find is called first, to select any found text. The selected
+-- text is then replaced by the specified replacement text.
+-- @param rtext The text to replace found text with. It can contain Lua escape
+-- sequences to use text captured by a Lua pattern. (%n where 1 <= n <= 9.)
+function find.replace(rtext)
+ if #buffer:get_sel_text() == 0 then return end
+ local buffer = buffer
+ buffer:target_from_selection()
+ if find.captures then
+ for i, v in ipairs(find.captures) do
+ rtext = rtext:gsub('[^%%]?[^%%]?%%'..i, v) -- not entirely correct
+ end
+ end
+ buffer:replace_target( rtext:gsub('\\[abfnrtv\\]', escapes) )
+end
+
+---
+-- Replaces all found text.
+-- This function is used by the find dialog. It is not recommended to call it
+-- via scripts.
+-- @param ftext The text to find.
+-- @param rtext The text to replace found text with.
+-- @param flags The number mask identical to the one in 'find'.
+-- @see find.find
+function find.replace_all(ftext, rtext, flags)
+ local count = 0
+ while( find.find(ftext, flags, true) ) do
+ find.replace(rtext)
+ count = count + 1
+ end
+ textadept.statusbar_text = tostring(count)..' replacement(s) made'
+end
diff --git a/core/ext/mime_types.lua b/core/ext/mime_types.lua
new file mode 100644
index 00000000..bf17219f
--- /dev/null
+++ b/core/ext/mime_types.lua
@@ -0,0 +1,86 @@
+-- Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+local languages = {
+ cpp = 'cpp',
+ css = 'css',
+ diff = 'diff',
+ html = 'html',
+ javascript = 'javascript',
+ lua = 'lua',
+ makefile = 'makefile',
+ php = 'php',
+ python = 'python',
+ rhtml = 'rhtml',
+ ruby = 'ruby',
+ xml = 'xml'
+}
+
+local l = languages
+local extensions = {
+ c = l.cpp, cpp = l.cpp, cxx = l.cpp, h = l.cpp,
+ css = l.css,
+ diff = l.diff, patch = l.diff,
+ html = l.html, htm = l.html, shtml = l.html,
+ iface = l.makefile,
+ js = l.javascript,
+ lua = l.lua,
+ mak = l.makefile, makefile = l.makefile, Makefile = l.makefile,
+ php = l.php,
+ py = l.python, pyw = l.python,
+ rhtml = l.rhtml,
+ rb = l.rb, rbw = l.rb,
+ xml = l.xml, xsl = l.xml, xslt = l.xml
+}
+
+---
+-- [Local] Sets the buffer's lexer language based on a filename.
+-- @param filename The filename used to set the lexer language.
+local function set_lexer_from_filename(filename)
+ local lexer
+ if filename then
+ local ext = filename:match('[^/]+$'):match('[^.]+$')
+ lexer = extensions[ext]
+ end
+ buffer:set_lexer_language(lexer or 'container')
+end
+
+---
+-- [Local] Loads a language module based on a filename (if it hasn't been
+-- loaded already).
+-- @param filename The filename used to load a language module from.
+local function load_language_module_from_filename(filename)
+ if not filename then return end
+ local ext = filename:match('[^/]+$'):match('[^.]+$')
+ local lang = extensions[ext]
+ if lang then
+ local ret, err = pcall(require, lang)
+ if ret then
+ modules[lang].set_buffer_properties()
+ elseif not ret and not err:match("^module '"..lang.."' not found:") then
+ textadept.handlers.error(err)
+ end
+ end
+end
+
+---
+-- [Local] Performs actions suitable for a new buffer.
+-- Sets the lexer language and loads the language module based on the new
+-- buffer's filename.
+local function handle_new()
+ local buffer = buffer
+ set_lexer_from_filename(buffer.filename)
+ load_language_module_from_filename(buffer.filename)
+end
+
+---
+-- [Local] Performs actions suitable for when buffers are switched.
+-- Sets the lexer language based on the current buffer's filename.
+local function handle_switch()
+ set_lexer_from_filename(buffer.filename)
+end
+
+local handlers = textadept.handlers
+handlers.add_function_to_handler('file_opened', handle_new)
+handlers.add_function_to_handler('file_saved_as', handle_new)
+handlers.add_function_to_handler('buffer_switch', handle_switch)
+handlers.add_function_to_handler('view_new', handle_switch)
diff --git a/core/ext/pm.lua b/core/ext/pm.lua
new file mode 100644
index 00000000..82274066
--- /dev/null
+++ b/core/ext/pm.lua
@@ -0,0 +1,95 @@
+-- Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+---
+-- Browsers loaded by the project manager.
+-- @class table
+-- @name browsers
+local browsers = { 'buffer_browser', 'file_browser', 'ctags_browser' }
+for _, b in ipairs(browsers) do require('ext/pm.'..b) end
+
+local pm = textadept.pm
+
+---
+-- Requests treeview contents from browser that matches pm_entry's text.
+-- This function is called internally and shouldn't be called by a script.
+-- @param full_path A numerically indexed table of treeview item parents. The
+-- first index contains the text of pm_entry. Subsequent indexes contain the
+-- ID's of parents of the child requested for expanding (if any).
+-- @param expanding Optional flag indicating if the contents of a parent are
+-- being requested. Defaults to false.
+-- @return table of tables to for display in the treeview (single level).
+-- Each key in the return table is the treeview item's ID. The table value
+-- has the following recognized fields:
+-- parent - boolean value indicating if this entry can contain children. If
+-- true, an expanding arrow is displayed next to the entry.
+-- pixbuf - a string representing a GTK stock-id whose icon is displayed
+-- next to an entry.
+-- text - the entry's Pango marked-up display text.
+-- Note that only a SINGLE level of data needs to be returned. When parents
+-- are expanded, this function is called again to get that level of data.
+function pm.get_contents_for(full_path, expanding)
+ for _, browser in pairs(pm.browsers) do
+ if browser.matches( full_path[1] ) then
+ return browser.get_contents_for(full_path, expanding)
+ end
+ end
+end
+
+---
+-- Performs an action based on the selected treeview item.
+-- This function is called internally and shouldn't be called by a script.
+-- @param selected_item Identical to 'full_path' in pm.get_contents_for.
+-- @see pm.get_contents_for
+function pm.perform_action(selected_item)
+ for _, browser in pairs(pm.browsers) do
+ if browser.matches( selected_item[1] ) then
+ return browser.perform_action(selected_item)
+ end
+ end
+end
+
+---
+-- Creates a context menu based on the selected treeview item.
+-- This function is called internally and shouldn't be called by a script.
+-- @param selected_item Identical to 'full_path' in pm.get_contents_for.
+-- @return table of menu items.
+-- The return table consists of an ordered list of strings to be used to
+-- construct a context menu. The strings are handled as follows:
+-- 'gtk-*' - a stock menu item is created based on the GTK stock-id.
+-- 'separator' - a menu separator item is created.
+-- Otherwise a regular menu item with a mnemonic is created.
+-- @see pm.get_contents_for
+function pm.get_context_menu(selected_item)
+ for _, browser in pairs(pm.browsers) do
+ if browser.matches( selected_item[1] ) then
+ return browser.get_context_menu(selected_item)
+ end
+ end
+end
+
+---
+-- Performs an action based on the selected menu item.
+-- This function is called internally and shouldn't be called by a script.
+-- @param menu_item The label text of the menu item selected.
+-- @param selected_item Identical to 'full_path' in pm.get_contents_for.
+-- @see pm.get_contents_for
+function pm.perform_menu_action(menu_item, selected_item)
+ for _, browser in pairs(pm.browsers) do
+ if browser.matches( selected_item[1] ) then
+ return browser.perform_menu_action(menu_item, selected_item)
+ end
+ end
+end
+
+---
+-- Toggles the width of the project manager.
+-- If the pm is visible, it's width is saved and then set to 0, effectively
+-- hiding it. If it is hidden, the width is restored.
+function pm.toggle_visible()
+ if pm.width > 0 then
+ pm.prev_width = pm.width
+ pm.width = 0
+ else
+ pm.width = pm.prev_width or 150
+ end
+end
diff --git a/core/ext/pm/buffer_browser.lua b/core/ext/pm/buffer_browser.lua
new file mode 100644
index 00000000..56891fa9
--- /dev/null
+++ b/core/ext/pm/buffer_browser.lua
@@ -0,0 +1,57 @@
+-- Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+---
+-- Buffer browser for the Textadept project manager.
+-- It is enabled with the prefix 'buffers' in the project manager entry field.
+module('textadept.pm.browsers.buffer', package.seeall)
+
+function matches(entry_text)
+ return entry_text:sub(1, 7) == 'buffers'
+end
+
+function get_contents_for()
+ local contents = {}
+ for index, buffer in ipairs(textadept.buffers) do
+ index = string.format("%02i", index)
+ contents[index] = {
+ pixbuf = buffer.dirty and 'gtk-edit' or 'gtk-file',
+ text = (buffer.filename or 'Untitled'):match('[^/]+$')
+ }
+ end
+ return contents
+end
+
+function perform_action(selected_item)
+ local index = selected_item[2]
+ local buffer = textadept.buffers[ tonumber(index) ]
+ if buffer then view:goto_buffer(index) view:focus() end
+end
+
+function get_context_menu(selected_item)
+ return { '_New', '_Open', '_Save', 'Save _As...', 'separator', '_Close' }
+end
+
+function perform_menu_action(menu_item, selected_item)
+ if menu_item == 'New' then
+ textadept.new_buffer()
+ elseif menu_item == 'Open' then
+ textadept.io.open()
+ elseif menu_item == 'Save' then
+ textadept.buffers[ tonumber( selected_item[2] ) ]:save()
+ elseif menu_item == 'Save As...' then
+ textadept.buffers[ tonumber( selected_item[2] ) ]:save_as()
+ elseif menu_item == 'Close' then
+ textadept.buffers[ tonumber( selected_item[2] ) ]:close()
+ end
+ textadept.pm.activate()
+end
+
+local add_function_to_handler = textadept.handlers.add_function_to_handler
+local function update_view()
+ if matches(textadept.pm.entry_text) then textadept.pm.activate() end
+end
+add_function_to_handler('file_opened', update_view)
+add_function_to_handler('buffer_new', update_view)
+add_function_to_handler('buffer_deleted', update_view)
+add_function_to_handler('save_point_reached', update_view)
+add_function_to_handler('save_point_left', update_view)
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)
diff --git a/core/ext/pm/file_browser.lua b/core/ext/pm/file_browser.lua
new file mode 100644
index 00000000..9acf9bec
--- /dev/null
+++ b/core/ext/pm/file_browser.lua
@@ -0,0 +1,64 @@
+-- Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+---
+-- File browser for the Textadept project manager.
+-- It is enabled by providing the absolute path to a directory in the project
+-- manager entry field.
+module('textadept.pm.browsers.file', package.seeall)
+
+function matches(entry_text)
+ return entry_text:sub(1, 1) == '/'
+end
+
+function get_contents_for(full_path)
+ local dirpath = table.concat(full_path, '/')
+ local out = io.popen('ls -1p "'..dirpath..'"'):read('*all')
+ if #out == 0 then
+ error('No such directory: '..dirpath)
+ return {}
+ end
+ local dir = {}
+ for entry in out:gmatch('[^\n]+') do
+ if entry:sub(-1, -1) == '/' then
+ local name = entry:sub(1, -2)
+ dir[name] = {
+ parent = true,
+ display_text = name,
+ pixbuf = 'gtk-directory'
+ }
+ else
+ dir[entry] = { display_text = entry }
+ end
+ end
+ return dir
+end
+
+function perform_action(selected_item)
+ local filepath = table.concat(selected_item, '/')
+ textadept.io.open(filepath)
+ view:focus()
+end
+
+function get_context_menu(selected_item)
+ return { '_Change Directory', 'File _Details' }
+end
+
+function perform_menu_action(menu_item, selected_item)
+ local filepath = table.concat(selected_item, '/')
+ if menu_item == 'Change Directory' then
+ textadept.pm.entry_text = filepath
+ textadept.pm.activate()
+ elseif menu_item == 'File Details' then
+ local out = io.popen('ls -dhl "'..filepath..'"'):read('*all')
+ local perms, num_dirs, owner, group, size, mod_date =
+ out:match('^(%S+) (%S+) (%S+) (%S+) (%S+) (%S+ %S)')
+ out = 'File details for:\n'..filepath..'\n'..
+ 'Perms:\t'..perms..'\n'..
+ '#Dirs:\t'..num_dirs..'\n'..
+ 'Owner:\t'..owner..'\n'..
+ 'Group:\t'..group..'\n'..
+ 'Size:\t'..size..'\n'..
+ 'Date:\t'..mod_date
+ text_input(out, nil, false, 250, 250)
+ end
+end