dtd.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. /*
  4. DTD mode
  5. Ported to CodeMirror by Peter Kroon <plakroon@gmail.com>
  6. Report bugs/issues here: https://github.com/codemirror/CodeMirror/issues
  7. GitHub: @peterkroon
  8. */
  9. ;(function (mod) {
  10. if (typeof exports == 'object' && typeof module == 'object')
  11. // CommonJS
  12. mod(require('../../lib/codemirror'))
  13. else if (typeof define == 'function' && define.amd)
  14. // AMD
  15. define(['../../lib/codemirror'], mod)
  16. // Plain browser env
  17. else mod(CodeMirror)
  18. })(function (CodeMirror) {
  19. 'use strict'
  20. CodeMirror.defineMode('dtd', function (config) {
  21. var indentUnit = config.indentUnit,
  22. type
  23. function ret(style, tp) {
  24. type = tp
  25. return style
  26. }
  27. function tokenBase(stream, state) {
  28. var ch = stream.next()
  29. if (ch == '<' && stream.eat('!')) {
  30. if (stream.eatWhile(/[\-]/)) {
  31. state.tokenize = tokenSGMLComment
  32. return tokenSGMLComment(stream, state)
  33. } else if (stream.eatWhile(/[\w]/)) return ret('keyword', 'doindent')
  34. } else if (ch == '<' && stream.eat('?')) {
  35. //xml declaration
  36. state.tokenize = inBlock('meta', '?>')
  37. return ret('meta', ch)
  38. } else if (ch == '#' && stream.eatWhile(/[\w]/)) return ret('atom', 'tag')
  39. else if (ch == '|') return ret('keyword', 'separator')
  40. else if (ch.match(/[\(\)\[\]\-\.,\+\?>]/)) return ret(null, ch)
  41. //if(ch === ">") return ret(null, "endtag"); else
  42. else if (ch.match(/[\[\]]/)) return ret('rule', ch)
  43. else if (ch == '"' || ch == "'") {
  44. state.tokenize = tokenString(ch)
  45. return state.tokenize(stream, state)
  46. } else if (stream.eatWhile(/[a-zA-Z\?\+\d]/)) {
  47. var sc = stream.current()
  48. if (sc.substr(sc.length - 1, sc.length).match(/\?|\+/) !== null) stream.backUp(1)
  49. return ret('tag', 'tag')
  50. } else if (ch == '%' || ch == '*') return ret('number', 'number')
  51. else {
  52. stream.eatWhile(/[\w\\\-_%.{,]/)
  53. return ret(null, null)
  54. }
  55. }
  56. function tokenSGMLComment(stream, state) {
  57. var dashes = 0,
  58. ch
  59. while ((ch = stream.next()) != null) {
  60. if (dashes >= 2 && ch == '>') {
  61. state.tokenize = tokenBase
  62. break
  63. }
  64. dashes = ch == '-' ? dashes + 1 : 0
  65. }
  66. return ret('comment', 'comment')
  67. }
  68. function tokenString(quote) {
  69. return function (stream, state) {
  70. var escaped = false,
  71. ch
  72. while ((ch = stream.next()) != null) {
  73. if (ch == quote && !escaped) {
  74. state.tokenize = tokenBase
  75. break
  76. }
  77. escaped = !escaped && ch == '\\'
  78. }
  79. return ret('string', 'tag')
  80. }
  81. }
  82. function inBlock(style, terminator) {
  83. return function (stream, state) {
  84. while (!stream.eol()) {
  85. if (stream.match(terminator)) {
  86. state.tokenize = tokenBase
  87. break
  88. }
  89. stream.next()
  90. }
  91. return style
  92. }
  93. }
  94. return {
  95. startState: function (base) {
  96. return { tokenize: tokenBase, baseIndent: base || 0, stack: [] }
  97. },
  98. token: function (stream, state) {
  99. if (stream.eatSpace()) return null
  100. var style = state.tokenize(stream, state)
  101. var context = state.stack[state.stack.length - 1]
  102. if (stream.current() == '[' || type === 'doindent' || type == '[') state.stack.push('rule')
  103. else if (type === 'endtag') state.stack[state.stack.length - 1] = 'endtag'
  104. else if (stream.current() == ']' || type == ']' || (type == '>' && context == 'rule')) state.stack.pop()
  105. else if (type == '[') state.stack.push('[')
  106. return style
  107. },
  108. indent: function (state, textAfter) {
  109. var n = state.stack.length
  110. if (textAfter.charAt(0) === ']') n--
  111. else if (textAfter.substr(textAfter.length - 1, textAfter.length) === '>') {
  112. if (textAfter.substr(0, 1) === '<') {
  113. } else if (type == 'doindent' && textAfter.length > 1) {
  114. } else if (type == 'doindent') n--
  115. else if (type == '>' && textAfter.length > 1) {
  116. } else if (type == 'tag' && textAfter !== '>') {
  117. } else if (type == 'tag' && state.stack[state.stack.length - 1] == 'rule') n--
  118. else if (type == 'tag') n++
  119. else if (textAfter === '>' && state.stack[state.stack.length - 1] == 'rule' && type === '>') n--
  120. else if (textAfter === '>' && state.stack[state.stack.length - 1] == 'rule') {
  121. } else if (textAfter.substr(0, 1) !== '<' && textAfter.substr(0, 1) === '>') n = n - 1
  122. else if (textAfter === '>') {
  123. } else n = n - 1
  124. //over rule them all
  125. if (type == null || type == ']') n--
  126. }
  127. return state.baseIndent + n * indentUnit
  128. },
  129. electricChars: ']>',
  130. }
  131. })
  132. CodeMirror.defineMIME('application/xml-dtd', 'dtd')
  133. })