Advertisement
Guest User

Untitled

a guest
Jun 29th, 2017
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.24 KB | None | 0 0
  1. --------------------------------------------------------------------------------
  2. -- The MIT License
  3. --
  4. -- Copyright (c) 2010 Brian Schott
  5. --
  6. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  7. -- of this software and associated documentation files (the "Software"), to deal
  8. -- in the Software without restriction, including without limitation the rights
  9. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. -- copies of the Software, and to permit persons to whom the Software is
  11. -- furnished to do so, subject to the following conditions:
  12. --
  13. -- The above copyright notice and this permission notice shall be included in
  14. -- all copies or substantial portions of the Software.
  15. --
  16. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR THERWISE, ARISING FROM,
  21. -- THE SOFTWARE.
  22. --------------------------------------------------------------------------------
  23.  
  24. --------------------------------------------------------------------------------
  25. -- This file provides functions that will automatically close XML tags.
  26. --
  27. -- See the comments above each function for the key bindings that should be used
  28. -- with the various functions. I do not recommend using autoTag in combination
  29. -- with completeClosingTagBracket. The two functions are designed for two
  30. -- different usage styles.
  31. --
  32. -- Changelog:
  33. --     Version 0.9.0 - Oct 26 2010
  34. --        * Added matching tag highlighting
  35. --     Version 0.8.1 - Aug 16 2010
  36. --        * Fixed a bug in one of the lua patterns. This has the nice
  37. --          side-effect of adding a "grow selection" feature to the
  38. --          selectToMatching function
  39. --     Version 0.8 - Aug 15 2010
  40. --         * Bug fix for unbalanced begin_undo_action / end_undo_action
  41. --         * Added the selectMatchingTagName function
  42. --         * Added the selectToMatching function
  43. --     Version 0.7 - Jun 25 2010
  44. --         * Added the ability to comment out text by selecting it and pressing
  45. --           the "!" key.
  46. --     Version 0.6 - Jun 02 2010
  47. --         * Fixed an infinite loop bug with the singleTag function
  48. --     Version 0.5 - May 17 2010
  49. --         * Added encloseTag function.
  50. --     Version 0.4 - May 15 2010
  51. --         * Added begin_undo_action and end_undo_action to various functions.
  52. --         * Updated documentation comments.
  53. --     Version 0.3 - May 11 2010
  54. --------------------------------------------------------------------------------
  55.  
  56.  
  57. MARK_XML_TAG_INDEX = 3
  58. MARK_XML_TAG_MATCH_COLOR = 0x00ff00
  59. MARK_XML_TAG_BAD_COLOR = 0x0000ff
  60. MARK_XML_TAG_ALPHA = 128
  61.  
  62. -- Sets view properties for highlighted word indicators and markers.
  63. local function set_highlight_properties()
  64.   local buffer = buffer
  65.   buffer.indic_fore[MARK_XML_TAG_INDEX] = MARK_XML_TAG_MATCH_COLOR
  66.   buffer.indic_style[MARK_XML_TAG_INDEX] = _SCINTILLA.constants.INDIC_ROUNDBOX
  67.   buffer.indic_alpha[MARK_XML_TAG_INDEX] = MARK_XML_TAG_ALPHA
  68.   buffer.indic_under[MARK_XML_TAG_INDEX] = true
  69. end
  70. if buffer then set_highlight_properties() end
  71. events.connect('view_new', set_highlight_properties)
  72.  
  73. local function clearMatchingTag()
  74.     local buffer = buffer
  75.     buffer.indicator_current = MARK_XML_TAG_INDEX
  76.     buffer.indic_fore[MARK_XML_TAG_INDEX] = MARK_XML_TAG_MATCH_COLOR
  77.     buffer:indicator_clear_range(0, buffer.length)
  78. end
  79.  
  80. --------------------------------------------------------------------------------
  81. -- Returns: start, end, type, name
  82. -- start is the start of the tag
  83. -- end is the end of the tag
  84. -- type is 0 for opening, 1 for closing, and 2 for single
  85. -- name is the tag name
  86. --------------------------------------------------------------------------------
  87. function currentTagInfo()
  88.     local buffer = buffer
  89.     buffer.target_end = 0
  90.     buffer.target_start = buffer.current_pos
  91.     buffer.search_flags = 0x00200000
  92.     local f = buffer:search_in_target("[<>]")
  93.     if f == -1 or buffer.char_at[f] ~= 60 then return nil end
  94.     buffer.target_end = buffer.length
  95.     buffer.target_start = buffer.current_pos
  96.     local e = buffer:search_in_target("[<>]")
  97.     if e == -1 or buffer.char_at[e] ~= 62 then return nil end
  98.     local text = buffer:text_range(f, e + 1)
  99.     local t
  100.     if text:find("^</") then
  101.         t = 1
  102.     elseif text:find("/>$") or text:find("^<!") then
  103.         t = 2
  104.     else
  105.         t = 0
  106.     end
  107.     local name = text:match("</?([^%s>]+)")
  108.     return f, e + 1, t, name
  109. end
  110. local currentTagInfo = currentTagInfo
  111.  
  112. --------------------------------------------------------------------------------
  113. -- Multi-selects the tag matching the one at the cursor position
  114. --------------------------------------------------------------------------------
  115. function highlightMatchingTagName()
  116.     local buffer = buffer
  117.     clearMatchingTag()
  118.     local first, last, tagType, tagName = currentTagInfo()
  119.     if first == nil or tagName == nil then return end
  120.     buffer:indicator_fill_range(first, last - first)
  121.     buffer.search_flags = 0x00200000
  122.     local depth
  123.     if tagType == 2 then
  124.         return
  125.     elseif tagType == 0 then
  126.         buffer.target_start = last
  127.         buffer.target_end = buffer.length
  128.         depth = 1
  129.     else
  130.         buffer.target_start = first
  131.         buffer.target_end = 0
  132.         depth = -1
  133.     end
  134.     local res
  135.     while depth ~= 0 do
  136.         res = buffer:search_in_target("</?"..tagName)
  137.         if res == -1 then break end
  138.         if buffer:text_range(res, res + 2) == "</" then
  139.             depth = depth - 1
  140.         else
  141.             depth = depth + 1
  142.         end
  143.         buffer.target_start = res + tagName:len()
  144.         if tagType == 0 then
  145.             buffer.target_end = buffer.length
  146.         else
  147.             buffer.target_end = 0
  148.         end
  149.     end
  150.     if depth ~= 0 then
  151.         buffer.indic_fore[MARK_XML_TAG_INDEX] = MARK_XML_TAG_BAD_COLOR
  152.         return
  153.     end
  154.     if tagType == 1 then
  155.         buffer.target_start = res
  156.         buffer.target_end = buffer.length
  157.         local len = buffer:search_in_target(">") - res + 1
  158.         buffer:indicator_fill_range(res, len)
  159.     else
  160.         buffer:indicator_fill_range(res, tagName:len() + 3)
  161.     end
  162. end
  163.  
  164. --------------------------------------------------------------------------------
  165. -- Selects all text within the tag containing the cursor
  166. --------------------------------------------------------------------------------
  167. function selectToMatching()
  168.     local buffer = buffer
  169.     local oldPos = buffer.current_pos
  170.     local pattern = "<[^>!?]*[^/>]>"
  171.     local count = 1
  172.     local tagName = nil
  173.     -- Search forward, finding all tags. Stop when an end tag is found that did
  174.     -- not have a matching opening tag during this search.
  175.     while count ~= 0 do
  176.         buffer:search_anchor()
  177.         -- Prevent an infinite loop in the (likely) event of malformed xml
  178.         if buffer:search_next(0x00200000, pattern) == -1 then
  179.             buffer:set_sel(oldPos, oldPos)
  180.             return
  181.         end
  182.         if buffer:get_sel_text():match("^</") then
  183.             count = count - 1
  184.         else
  185.             count = count + 1
  186.         end
  187.         -- Note the name of the ending tag so that the next loop can run a bit
  188.         -- faster.
  189.         tagName = buffer:get_sel_text():match("^</(%S+)>$")
  190.         buffer:char_right()
  191.     end
  192.     -- Save the end position of the ending tag
  193.     local endPos = buffer.current_pos
  194.     -- Back up the position of the cursor to before the ending tag
  195.     buffer:search_anchor()
  196.     buffer:search_prev(4, "<")
  197.     -- Search for the matching opening tag
  198.     local startPattern = "</?"..tagName
  199.     count = 1
  200.     while count ~= 0 do
  201.         buffer:search_anchor()
  202.         -- Prevent an infinite loop in the (likely) event of malformed xml
  203.         if buffer:search_prev(0x00200000, startPattern) == -1 then
  204.             buffer:set_sel(oldPos, oldPos)
  205.             return
  206.         end
  207.         if buffer:get_sel_text():match("</") then
  208.             count = count + 1
  209.         else
  210.             count = count - 1
  211.         end
  212.     end
  213.     local startPos = buffer.current_pos
  214.     -- Set the selection
  215.     buffer:set_sel(startPos, endPos)
  216. end
  217.  
  218. --------------------------------------------------------------------------------
  219. -- For internal use. Returns the name of any tag open at the cursor position
  220. --------------------------------------------------------------------------------
  221. local function findOpenTag()
  222.     local buffer = buffer
  223.     local stack = {}
  224.     local endLine = buffer:line_from_position(buffer.current_pos)
  225.     for i = 0, endLine do
  226.         local first = 0;
  227.         local last = 0;
  228.         text = buffer:get_line(i)
  229.         first, last = text:find("</?[^%s>%?!]+.->", last)
  230.         while first ~= nil and text:sub(first, last):find("/>") == nil do
  231.             local tagName = text:match("<(/?[^%s>%?!]+).->", first)
  232.             if tagName:find("^/") then
  233.                 if #stack == 0 then
  234.                     return nil
  235.                 elseif "/"..stack[#stack] == tagName then
  236.                     table.remove(stack)
  237.                 else
  238.                     break
  239.                 end
  240.             else
  241.                 table.insert(stack, tagName)
  242.             end
  243.             first, last = text:find("</?[^%s>%?!]+.->", last)
  244.         end
  245.     end
  246.     return stack[#stack]
  247. end
  248.  
  249. --------------------------------------------------------------------------------
  250. -- Call this one whenever you want a tag closed
  251. --------------------------------------------------------------------------------
  252. function completeClosingTag()
  253.     buffer:begin_undo_action()
  254.     local buffer = buffer
  255.     local tagName = findOpenTag()
  256.     if tagName ~= nil then
  257.         buffer:add_text("</"..tagName..">")
  258.     end
  259.     buffer:end_undo_action()
  260. end
  261.  
  262. --------------------------------------------------------------------------------
  263. -- Call this function when a '>' character is inserted. It is not recommended to
  264. -- use this with autoTag.
  265. --
  266. -- ['>'] = {completeClosingTagBracket},
  267. --------------------------------------------------------------------------------
  268. function completeClosingTagBracket()
  269.     buffer:begin_undo_action()
  270.     local buffer = buffer
  271.     local pos = buffer.current_pos
  272.     buffer:insert_text(pos, ">")
  273.     local tagName = findOpenTag()
  274.     if tagName ~= nil then
  275.         buffer:set_selection(pos + 1, pos + 1)
  276.         buffer:add_text("</"..tagName..">")
  277.     end
  278.     buffer:set_sel(pos + 1, pos + 1)
  279.     buffer:end_undo_action()
  280. end
  281.  
  282. --------------------------------------------------------------------------------
  283. -- Uses the multiple-cursor feature to close tags as you type them. It is not
  284. -- recommended to use this with completeClosingTagBracket
  285. --
  286. -- ['<'] = {autoTag},
  287. --------------------------------------------------------------------------------
  288. function autoTag()
  289.     buffer:begin_undo_action()
  290.     local pos = buffer.current_pos
  291.     buffer:insert_text(pos, "<></>")
  292.     buffer:set_selection(pos + 4, pos + 4, 0)
  293.     buffer:add_selection(pos + 1, pos + 1)
  294.     buffer:end_undo_action()
  295. end
  296.  
  297. local function toggleComment()
  298.     buffer:begin_undo_action()
  299.     local text = buffer:get_sel_text()
  300.     local first = text:match("<!%-%-(.-)%-%->")
  301.     if first == nil then
  302.         buffer:replace_sel("<!--"..text.."-->")
  303.     else
  304.         buffer:replace_sel(first)
  305.     end
  306.     buffer:end_undo_action()
  307. end
  308.  
  309. --------------------------------------------------------------------------------
  310. -- Opens an XML/HTML comment. Use this only if using autoTag.
  311. --
  312. -- ['!'] = {completeComment},
  313. --------------------------------------------------------------------------------
  314. function completeComment()
  315.     buffer:begin_undo_action()
  316.     local text = buffer:get_sel_text()
  317.     if #text > 0 then
  318.         toggleComment()
  319.     else
  320.         local pos = buffer.current_pos
  321.         buffer:set_selection(pos, pos + 4)
  322.         if buffer:get_sel_text() == "></>" then
  323.             buffer:replace_sel("!--  -->")
  324.             buffer:set_selection(pos + 4, pos + 4)
  325.         else
  326.             buffer:set_selection(pos, pos)
  327.             buffer:add_text("!")
  328.         end
  329.     end
  330.     buffer:end_undo_action()
  331. end
  332.  
  333. --------------------------------------------------------------------------------
  334. -- Call this in response to a ? being inserted. Use this only if using autoTag.
  335. --
  336. -- ['?'] = {completePHP},
  337. --------------------------------------------------------------------------------
  338. function completePHP()
  339.     buffer:begin_undo_action()
  340.     local pos = buffer.current_pos
  341.     buffer:set_selection(pos, pos + 4)
  342.     if buffer:get_sel_text() == "></>" then
  343.         buffer:replace_sel("?php  ?>")
  344.         buffer:set_selection(pos + 5, pos + 5)
  345.     else
  346.         buffer:set_selection(pos, pos)
  347.         buffer:add_text("?")
  348.     end
  349.     buffer:end_undo_action()
  350. end
  351.  
  352. --------------------------------------------------------------------------------
  353. -- Bind this to a '/' character being inserted. This cancels the above function
  354. -- for tags like <br/>. Use this only if using autoTag.
  355. --
  356. -- ['/'] = {singleTag},
  357. --------------------------------------------------------------------------------
  358. function singleTag()
  359.     buffer:begin_undo_action()
  360.     if buffer.selections > 1 then
  361.         local pos = buffer.current_pos
  362.         buffer:set_sel(pos - 1, pos)
  363.         if buffer:get_sel_text() =="<" then
  364.             buffer:set_selection(pos, pos + 4)
  365.             if buffer:get_sel_text() == "></>" then
  366.                 buffer:replace_sel("/>")
  367.                 buffer:set_selection(pos + 1, pos + 1)
  368.                 buffer:end_undo_action()
  369.                 return
  370.             else
  371.                 buffer:set_selection(pos, pos)
  372.             end
  373.         else
  374.             buffer:set_selection(pos, pos)
  375.         end
  376.         buffer:set_sel(pos, pos + 2)
  377.         local text = buffer:get_sel_text()
  378.         if text == "><" then
  379.             local doKill = true
  380.             while text:find("></[^>%s]+>") == nil do
  381.                 if buffer.selection_end == buffer.line_end_position then
  382.                     doKill = false
  383.                 end
  384.                 buffer:char_right_extend()
  385.                 text = buffer:get_sel_text()
  386.             end
  387.             buffer:replace_sel("/>")
  388.             buffer:set_selection(pos, pos)
  389.         else
  390.             buffer:set_selection(pos, pos)
  391.             buffer:add_text("/")
  392.         end
  393.     else
  394.         buffer:add_text("/")
  395.     end
  396.     buffer:end_undo_action()
  397. end
  398.  
  399. function handleBackspace()
  400.     if buffer.selections == 2 then
  401.         buffer:begin_undo_action()
  402.         local pos1 = buffer.current_pos
  403.         buffer:rotate_selection()
  404.         local pos2 = buffer.current_pos
  405.         buffer:set_sel(pos1 - 1, pos1 + 5)
  406.         if buffer:get_sel_text() == "<></>" then
  407.             buffer:replace_sel("")
  408.             buffer:goto_pos(pos1)
  409.             buffer:end_undo_action()
  410.         else
  411.             buffer:set_sel(pos2, pos2)
  412.             buffer:add_selection(pos1, pos1)
  413.             buffer:end_undo_action()
  414.             return false
  415.         end
  416.     else
  417.         return false
  418.     end
  419. end
  420.  
  421. --------------------------------------------------------------------------------
  422. -- Call this when the spacebar is pressed. Use this only if using autoTag.
  423. --
  424. -- [' '] = {handleSpace},
  425. --------------------------------------------------------------------------------
  426. function handleSpace()
  427.     buffer:begin_undo_action()
  428.     local pos = buffer.current_pos
  429.     if buffer.selections > 1 then
  430.         buffer:clear_selections()
  431.         buffer:set_sel(pos, pos)
  432.         buffer:add_text(" ")
  433.     else
  434.         if #buffer:get_sel_text() > 0 then
  435.             buffer:replace_sel(" ")
  436.         else
  437.             buffer:add_text(" ")
  438.         end
  439.     end
  440.     buffer:end_undo_action()
  441. end
  442.  
  443. --------------------------------------------------------------------------------
  444. -- Enclose the selection in a tag
  445. --------------------------------------------------------------------------------
  446. function encloseTag()
  447.     buffer:begin_undo_action()
  448.     local text = buffer:get_sel_text()
  449.     local start = buffer.selection_start
  450.     buffer:replace_sel("<>"..text.."</>")
  451.     local leftCursorPos = start + 1
  452.     local rightCursorPos = start + 4 + #text
  453.     buffer:set_selection(rightCursorPos, rightCursorPos)
  454.     buffer:add_selection(leftCursorPos, leftCursorPos)
  455.     buffer:end_undo_action()
  456. end
  457.  
  458. --------------------------------------------------------------------------------
  459. -- Toggles line comments on the selected lines
  460. --------------------------------------------------------------------------------
  461. function toggleLineComment()
  462.     buffer:begin_undo_action()
  463.     local initial = buffer:line_from_position(buffer.current_pos)
  464.     local first = initial
  465.     local last = buffer:line_from_position(buffer.anchor)
  466.     if first > last then first, last = last, first end
  467.     for i = first, last do
  468.         buffer:goto_line(i)
  469.         buffer:home()
  470.         buffer:line_end_extend()
  471.         toggleComment()
  472.     end
  473.     buffer:goto_line(initial)
  474.     buffer:end_undo_action()
  475. end
  476.  
  477. local pcall = pcall
  478. local highlightMatchingTagName = highlightMatchingTagName
  479.  
  480. -- This variable helps prevent stack overflows
  481. local lockdepth = 0
  482. function highlightEvent()
  483.     local buffer = buffer
  484.     lockdepth = lockdepth + 1
  485.     if lockdepth == 1 then
  486.         local ok, lexLang = pcall(buffer.get_lexer, buffer)
  487.         if lexLang == "xml" or lexLang == "hypertext" or lexLang == "php" then
  488.             -- Use pcall because it's impossible to guarantee that the buffer
  489.             -- will be valid when calling highlightMatchingTagName
  490.             pcall(highlightMatchingTagName)
  491.         end
  492.     end
  493.     lockdepth = lockdepth - 1
  494. end
  495. events.connect("update_ui", highlightEvent)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement