PageRenderTime 26ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/luaannotate.lua

http://github.com/davidm/lua-annotate
Lua | 170 lines | 130 code | 19 blank | 21 comment | 19 complexity | a6b494f723327a72cebc7fbd7055500d MD5 | raw file
  1. -- luaannotate.lua
  2. -- (C) 2010, David Manura
  3. -- Load annotations file.
  4. local function load_annotations(path)
  5. local filename = path:match'[^\\/]+$'
  6. if not filename then
  7. error('Invalid path ' .. path)
  8. end
  9. local annotations = {}
  10. local id
  11. local text = ''
  12. local function commit()
  13. if id then
  14. --print('DEBUG-luacomment-commit', id, text)
  15. text = text:gsub('[\r\n]$', '')
  16. annotations[id] = text
  17. end
  18. end
  19. -- Process line-by-line.
  20. local fh = assert(io.open(path))
  21. local linenum = 1
  22. for line in fh:lines() do
  23. if line:match'^#' then
  24. commit()
  25. local sfile, sname = line:match'^#%s*([^:]+):([%w_]*)'
  26. if not sfile then
  27. error('Invalid directive in ' .. path .. ':' .. linenum)
  28. end
  29. text = ''
  30. id = sfile .. ':' .. sname
  31. else
  32. text = text .. (text ~= '' and '\n' or '') .. line
  33. end
  34. linenum = linenum + 1
  35. end
  36. commit()
  37. return annotations
  38. end
  39. -- Locates where annotations should go in source string `code`
  40. -- in file path `path`.
  41. local function place_annotations(code, path, annotations)
  42. local sfile = path:match'[^\\/]+$'
  43. local notes = {}
  44. -- Comments on functions/macros/structs
  45. -- TODO: could be improved (robustness)
  46. local iline = 1
  47. for line in (code .. '\n'):gmatch'(.-)\r?\n' do
  48. local sname =
  49. line:match'^[%w_ ]-[%w_*]+%s([%w_]+)%s*%b()%s*%{?$' or
  50. line:match'^#define%s+([%w_]+)' or
  51. line:match'^typedef%s+struct%s+([%w_]+)' or
  52. line:match'^typedef%s+union%s+([%w_]+)' or
  53. line:match'^typedef%s+.*%s+([%w_]+);'
  54. if sname then
  55. local id = sfile .. ':' .. sname
  56. local comment = annotations[id]
  57. --print('DEBUG-luacomment-comment', comment, id)
  58. if comment then
  59. table.insert(notes, {iline, comment})
  60. end
  61. end
  62. iline = iline + 1
  63. end
  64. -- Comments on file.
  65. local filecomment = annotations[sfile .. ':']
  66. if filecomment then
  67. if notes[1] and notes[1][1] == 1 then -- existing comment on line 1
  68. local comment = filecomment .. '\n\n' .. notes[1][2]
  69. notes[1][2] = comment
  70. else
  71. table.insert(notes, 1, {1, filecomment})
  72. end
  73. end
  74. return notes
  75. end
  76. -- Utility function to check whether file exists (actually, is readable).
  77. local function exists_file(path)
  78. local fh = io.open(path)
  79. if fh then fh:close() return true end
  80. return false
  81. end
  82. -- Utility function to load file into string (text mode).
  83. local function read_file(path)
  84. local fh, err = io.open(path)
  85. if not fh then
  86. error('Could not open ' .. path .. ' : ' .. err)
  87. end
  88. local data = fh:read'*a'
  89. fh:close()
  90. return data
  91. end
  92. -- SciTE specific
  93. if scite_OnOpen then -- if ExtMan installed
  94. -- Add annotation notes to SciTE buffer.
  95. local function scite_annotate_buffer(notes)
  96. editor.AnnotationVisible = 2
  97. for _, note in ipairs(notes) do
  98. local iline, comment = unpack(note)
  99. editor:AnnotationSetText(iline-1, comment)
  100. --editor:AnnotationStyle[iline] = 1
  101. end
  102. end
  103. -- SciTE ExtMan callback function, on file opened.
  104. scite_OnOpen(function(path)
  105. local annotations_path = path:gsub('[^\\/]+$', 'annotations.txt')
  106. print('DEBUG-luacomment-open', path, annotations_path)
  107. if exists_file(annotations_path) then
  108. local code = editor:GetText()
  109. local annotations = load_annotations(annotations_path)
  110. local notes = place_annotations(code, path, annotations)
  111. scite_annotate_buffer(notes)
  112. end
  113. end)
  114. return
  115. end
  116. -- Escapes characters in text to allow embedding inside C comment (/* */).
  117. local function cpp_escape_comment(text)
  118. text = text:gsub('%*/', '* /')
  119. return text
  120. end
  121. -- Adds annotations to code.
  122. local function cpp_annotate(code, notes)
  123. local commentofline = {} -- index
  124. for _, linecomment in ipairs(notes) do
  125. local iline, comment = unpack(linecomment)
  126. commentofline[iline] = comment
  127. end
  128. -- TODO: could be improved: avoid nested comments
  129. local iline = 1
  130. code = (code .. '\n'):gsub('(.-\r?\n)', function(line)
  131. if commentofline[iline] then
  132. line = '/*# ' .. cpp_escape_comment(commentofline[iline]) .. '*/\n' .. line
  133. end
  134. iline = iline + 1
  135. return line
  136. end)
  137. return code
  138. end
  139. -- Command-line usage: lua luacomment.lua <filename>
  140. local path = ...
  141. if path then
  142. local annotations_path = path:gsub('[^\\/]+$', 'annotations.txt')
  143. local code = read_file(path)
  144. local annotations = load_annotations(annotations_path)
  145. local notes = place_annotations(code, path, annotations)
  146. code = cpp_annotate(code, notes)
  147. io.stdout:write(code)
  148. else
  149. io.stderr:write('usage: luaannotate.lua <filename>')
  150. os.exit(1)
  151. end