Skip to content

Commit ba27fdc

Browse files
committed
Initial release
1 parent 79e1ccb commit ba27fdc

4 files changed

Lines changed: 219 additions & 2 deletions

File tree

README.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,31 @@
1-
# xml-mode
2-
XML support for Text Forge code editor
1+
# Text Forge XML Mode
2+
Official XML (Extensible Markup Language) mdoe for Text Forge code editor
3+
4+
---
5+
6+
XML Mode is a lightweight and functional mode for Text Forge code editor. It's a part of official Text Forge modes.
7+
8+
## Mode Features
9+
- Updated to Text Forge Mode API V2.2
10+
- Outline system with all tags
11+
- Simple code highlighter
12+
- Simple code completion with current file tags
13+
- Advanced auto formatting
14+
- Parse linting support
15+
16+
## Setup
17+
Supports Text Forge 0.2 and newer versions.
18+
19+
### Method 1: From releases
20+
- Download `xml.tfmode` file from releases.
21+
- Open Text Forge and go to Settings > Mode Manager > Import Mode and select mode file.
22+
- Mode is ready to use. Please restart editor for safer experience.
23+
24+
### Method 2: From repo
25+
- Download repo as zip and extract it.
26+
- Open editor data folder (use Settings > Open Data Folder)
27+
- Copy `modes/` folder content to data `modes/`.
28+
- Restart editor.
29+
30+
### Development
31+
This project is under Text Forge organization and MIT license, so all contribution and development process is same as Text Forge project, for more information see [online docs](https://text-forge.github.io/docs).

modes/xml/icon.png

1.55 KB
Loading

modes/xml/mode.cfg

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[mode]
2+
3+
name="XML"
4+
description="XML (Extensible Markup Language) support for Text Forge"
5+
author="Text Forge Team"
6+
version="1.0.0"
7+
extensions=["xml", "xsd", "dtd", "rss", "atom", "svg", "xhtml", "opf", "kml", "collada", "dae"]

modes/xml/mode.gd

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
extends TextForgeMode
2+
3+
func _initialize_mode() -> Error:
4+
_initialize_highlighter()
5+
comment_delimiters.append({
6+
"start_key": "<!--",
7+
"end_key": "-->",
8+
"line_only": false,
9+
})
10+
string_delimiters.append({
11+
"start_key": "<![",
12+
"end_key": "]>",
13+
"line_only": false,
14+
})
15+
string_delimiters.append({
16+
"start_key": '"',
17+
"end_key": '"',
18+
"line_only": false,
19+
})
20+
string_delimiters.append({
21+
"start_key": "'",
22+
"end_key": "'",
23+
"line_only": false,
24+
})
25+
26+
_enable_auto_format_feature()
27+
28+
return OK
29+
30+
31+
func _auto_format(text: String) -> String:
32+
var parser := XMLParser.new()
33+
var err := parser.open_buffer(text.to_utf8_buffer())
34+
if err:
35+
Global.send_notification(
36+
Global.Notification.ERROR,
37+
"Failed to parse XML for auto formatting!",
38+
"Error code: " + str(err)
39+
)
40+
return text
41+
42+
var indent_level := 0
43+
var indent_str := " ".repeat(Global.get_editor().indent_size) if Global.get_editor().indent_use_spaces else "\t"
44+
var formatted := []
45+
46+
while parser.read() == OK:
47+
var new_line := ""
48+
match parser.get_node_type():
49+
XMLParser.NODE_ELEMENT:
50+
new_line = indent_str.repeat(indent_level) + "<" + parser.get_node_name()
51+
for i in parser.get_attribute_count():
52+
new_line += " %s=\"%s\"" % [parser.get_attribute_name(i), parser.get_attribute_value(i)]
53+
if parser.is_empty():
54+
new_line += "/>"
55+
else:
56+
new_line += ">"
57+
indent_level += 1
58+
XMLParser.NODE_ELEMENT_END:
59+
indent_level -= 1
60+
new_line = indent_str.repeat(indent_level) + "</" + parser.get_node_name() + ">"
61+
XMLParser.NODE_TEXT:
62+
var node_text := parser.get_node_data().strip_edges()
63+
if not node_text.is_empty():
64+
new_line = indent_str.repeat(indent_level) + node_text
65+
XMLParser.NODE_COMMENT:
66+
new_line = indent_str.repeat(indent_level) + "<!--" + parser.get_node_data() + "-->"
67+
XMLParser.NODE_CDATA:
68+
new_line = indent_str.repeat(indent_level) + "<![CDATA[%s]]" % parser.get_node_data()
69+
if not new_line.is_empty():
70+
formatted.append(new_line)
71+
72+
return "\n".join(formatted)
73+
74+
75+
func _update_code_completion_options(text: String) -> void:
76+
var parser := XMLParser.new()
77+
var err := parser.open_buffer(text.to_utf8_buffer())
78+
if err:
79+
return
80+
81+
var tags := []
82+
83+
while parser.read() == OK:
84+
if parser.get_node_type() in [XMLParser.NODE_ELEMENT, XMLParser.NODE_ELEMENT_END]:
85+
var node_name := parser.get_node_name()
86+
if not(node_name.is_empty() or tags.has(node_name)):
87+
tags.append(node_name)
88+
89+
var color := U.get_syntax_color(U.SyntaxColors.FUNCTION_DEF)
90+
91+
for t in tags:
92+
Global.get_editor().add_code_completion_option(CodeEdit.KIND_CLASS, "<" + t + ">", t + ">", color, null, null, 0)
93+
Global.get_editor().add_code_completion_option(CodeEdit.KIND_CLASS, "</" + t + ">", "/" + t + ">", color, null, null, 0)
94+
Global.get_editor().add_code_completion_option(CodeEdit.KIND_CLASS, "<" + t + "/>", t + "/>", color, null, null, 0)
95+
96+
97+
func _generate_outline(text: String) -> Array:
98+
var parser = XMLParser.new()
99+
var err := parser.open_buffer(text.to_utf8_buffer())
100+
if err:
101+
return []
102+
103+
var outline := []
104+
var stack := []
105+
106+
while parser.read() == OK:
107+
match parser.get_node_type():
108+
XMLParser.NODE_ELEMENT:
109+
var node_name = parser.get_node_name()
110+
var line = parser.get_current_line()
111+
var node := [node_name, line]
112+
113+
if stack.size() > 0:
114+
stack[-1].append(node)
115+
else:
116+
outline.append(node)
117+
118+
if not parser.is_empty():
119+
stack.append(node)
120+
121+
XMLParser.NODE_ELEMENT_END:
122+
if stack.size() > 0:
123+
stack.pop_back()
124+
125+
return outline
126+
127+
128+
func _lint_file(text: String) -> Array[Dictionary]:
129+
var problems: Array[Dictionary] = []
130+
var parser := XMLParser.new()
131+
var err := parser.open_buffer(text.to_utf8_buffer())
132+
if err:
133+
problems.append({
134+
"line": 0,
135+
"column": -1,
136+
"error": true,
137+
"title": "Parse error",
138+
"details": "Failed to parse XML (error code %d)." % err
139+
})
140+
return problems
141+
142+
while true:
143+
var result := parser.read()
144+
match result:
145+
OK:
146+
pass
147+
ERR_FILE_EOF:
148+
break
149+
_:
150+
problems.append({
151+
"line": parser.get_current_line(),
152+
"column": -1,
153+
"error": true,
154+
"title": "Malformed XML",
155+
"details": "Unexpected parse error code %d." % result
156+
})
157+
break
158+
match parser.get_node_type():
159+
XMLParser.NODE_ELEMENT:
160+
if parser.get_node_name().is_empty():
161+
problems.append({
162+
"line": parser.get_current_line(),
163+
"column": -1,
164+
"error": true,
165+
"title": "Empty tag name",
166+
"details": "An element with no name found."
167+
})
168+
return problems
169+
170+
171+
func _initialize_highlighter() -> void:
172+
syntax_highlighter = CodeHighlighter.new()
173+
syntax_highlighter.number_color = U.get_syntax_color(U.SyntaxColors.NUMBER)
174+
syntax_highlighter.symbol_color = U.get_syntax_color(U.SyntaxColors.SYMBOL)
175+
syntax_highlighter.function_color = Color.WHITE
176+
syntax_highlighter.member_variable_color = U.get_syntax_color(U.SyntaxColors.MEMBER)
177+
syntax_highlighter.add_color_region('<!--', '-->', U.get_syntax_color(U.SyntaxColors.COMMENT), false)
178+
syntax_highlighter.add_color_region('"', '"', U.get_syntax_color(U.SyntaxColors.STRING), false)
179+
syntax_highlighter.add_color_region("'", "'", U.get_syntax_color(U.SyntaxColors.STRING), false)
180+
syntax_highlighter.add_color_region('<', '>', U.get_syntax_color(U.SyntaxColors.FUNCTION_DEF), false)
181+
syntax_highlighter.add_color_region('<![', ']>', U.get_syntax_color(U.SyntaxColors.STRING), false)

0 commit comments

Comments
 (0)