1 | // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team |
---|
2 | // |
---|
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this |
---|
4 | // software and associated documentation files (the "Software"), to deal in the Software |
---|
5 | // without restriction, including without limitation the rights to use, copy, modify, merge, |
---|
6 | // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
---|
7 | // to whom the Software is furnished to do so, subject to the following conditions: |
---|
8 | // |
---|
9 | // The above copyright notice and this permission notice shall be included in all copies or |
---|
10 | // substantial portions of the Software. |
---|
11 | // |
---|
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
---|
13 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
---|
14 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
---|
15 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
---|
16 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
---|
17 | // DEALINGS IN THE SOFTWARE. |
---|
18 | |
---|
19 | using System; |
---|
20 | using System.Collections.Generic; |
---|
21 | using System.Collections.ObjectModel; |
---|
22 | using System.Diagnostics; |
---|
23 | using System.IO; |
---|
24 | using System.Xml; |
---|
25 | using ICSharpCode.AvalonEdit.Utils; |
---|
26 | |
---|
27 | namespace ICSharpCode.AvalonEdit.Highlighting |
---|
28 | { |
---|
29 | /// <summary> |
---|
30 | /// Manages a list of syntax highlighting definitions. |
---|
31 | /// </summary> |
---|
32 | /// <remarks> |
---|
33 | /// All memers on this class (including instance members) are thread-safe. |
---|
34 | /// </remarks> |
---|
35 | public class HighlightingManager : IHighlightingDefinitionReferenceResolver |
---|
36 | { |
---|
37 | sealed class DelayLoadedHighlightingDefinition : IHighlightingDefinition |
---|
38 | { |
---|
39 | readonly object lockObj = new object(); |
---|
40 | readonly string name; |
---|
41 | Func<IHighlightingDefinition> lazyLoadingFunction; |
---|
42 | IHighlightingDefinition definition; |
---|
43 | Exception storedException; |
---|
44 | |
---|
45 | public DelayLoadedHighlightingDefinition(string name, Func<IHighlightingDefinition> lazyLoadingFunction) |
---|
46 | { |
---|
47 | this.name = name; |
---|
48 | this.lazyLoadingFunction = lazyLoadingFunction; |
---|
49 | } |
---|
50 | |
---|
51 | public string Name { |
---|
52 | get { |
---|
53 | if (name != null) |
---|
54 | return name; |
---|
55 | else |
---|
56 | return GetDefinition().Name; |
---|
57 | } |
---|
58 | } |
---|
59 | |
---|
60 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", |
---|
61 | Justification = "The exception will be rethrown")] |
---|
62 | IHighlightingDefinition GetDefinition() |
---|
63 | { |
---|
64 | Func<IHighlightingDefinition> func; |
---|
65 | lock (lockObj) { |
---|
66 | if (this.definition != null) |
---|
67 | return this.definition; |
---|
68 | func = this.lazyLoadingFunction; |
---|
69 | } |
---|
70 | Exception exception = null; |
---|
71 | IHighlightingDefinition def = null; |
---|
72 | try { |
---|
73 | using (var busyLock = BusyManager.Enter(this)) { |
---|
74 | if (!busyLock.Success) |
---|
75 | throw new InvalidOperationException("Tried to create delay-loaded highlighting definition recursively. Make sure the are no cyclic references between the highlighting definitions."); |
---|
76 | def = func(); |
---|
77 | } |
---|
78 | if (def == null) |
---|
79 | throw new InvalidOperationException("Function for delay-loading highlighting definition returned null"); |
---|
80 | } catch (Exception ex) { |
---|
81 | exception = ex; |
---|
82 | } |
---|
83 | lock (lockObj) { |
---|
84 | this.lazyLoadingFunction = null; |
---|
85 | if (this.definition == null && this.storedException == null) { |
---|
86 | this.definition = def; |
---|
87 | this.storedException = exception; |
---|
88 | } |
---|
89 | if (this.storedException != null) |
---|
90 | throw new HighlightingDefinitionInvalidException("Error delay-loading highlighting definition", this.storedException); |
---|
91 | return this.definition; |
---|
92 | } |
---|
93 | } |
---|
94 | |
---|
95 | public HighlightingRuleSet MainRuleSet { |
---|
96 | get { |
---|
97 | return GetDefinition().MainRuleSet; |
---|
98 | } |
---|
99 | } |
---|
100 | |
---|
101 | public HighlightingRuleSet GetNamedRuleSet(string name) |
---|
102 | { |
---|
103 | return GetDefinition().GetNamedRuleSet(name); |
---|
104 | } |
---|
105 | |
---|
106 | public HighlightingColor GetNamedColor(string name) |
---|
107 | { |
---|
108 | return GetDefinition().GetNamedColor(name); |
---|
109 | } |
---|
110 | |
---|
111 | public IEnumerable<HighlightingColor> NamedHighlightingColors { |
---|
112 | get { |
---|
113 | return GetDefinition().NamedHighlightingColors; |
---|
114 | } |
---|
115 | } |
---|
116 | |
---|
117 | public override string ToString() |
---|
118 | { |
---|
119 | return this.Name; |
---|
120 | } |
---|
121 | |
---|
122 | public IDictionary<string, string> Properties { |
---|
123 | get { |
---|
124 | return GetDefinition().Properties; |
---|
125 | } |
---|
126 | } |
---|
127 | } |
---|
128 | |
---|
129 | readonly object lockObj = new object(); |
---|
130 | Dictionary<string, IHighlightingDefinition> highlightingsByName = new Dictionary<string, IHighlightingDefinition>(); |
---|
131 | Dictionary<string, IHighlightingDefinition> highlightingsByExtension = new Dictionary<string, IHighlightingDefinition>(StringComparer.OrdinalIgnoreCase); |
---|
132 | List<IHighlightingDefinition> allHighlightings = new List<IHighlightingDefinition>(); |
---|
133 | |
---|
134 | /// <summary> |
---|
135 | /// Gets a highlighting definition by name. |
---|
136 | /// Returns null if the definition is not found. |
---|
137 | /// </summary> |
---|
138 | public IHighlightingDefinition GetDefinition(string name) |
---|
139 | { |
---|
140 | lock (lockObj) { |
---|
141 | IHighlightingDefinition rh; |
---|
142 | if (highlightingsByName.TryGetValue(name, out rh)) |
---|
143 | return rh; |
---|
144 | else |
---|
145 | return null; |
---|
146 | } |
---|
147 | } |
---|
148 | |
---|
149 | /// <summary> |
---|
150 | /// Gets a copy of all highlightings. |
---|
151 | /// </summary> |
---|
152 | public ReadOnlyCollection<IHighlightingDefinition> HighlightingDefinitions { |
---|
153 | get { |
---|
154 | lock (lockObj) { |
---|
155 | return Array.AsReadOnly(allHighlightings.ToArray()); |
---|
156 | } |
---|
157 | } |
---|
158 | } |
---|
159 | |
---|
160 | /// <summary> |
---|
161 | /// Gets a highlighting definition by extension. |
---|
162 | /// Returns null if the definition is not found. |
---|
163 | /// </summary> |
---|
164 | public IHighlightingDefinition GetDefinitionByExtension(string extension) |
---|
165 | { |
---|
166 | lock (lockObj) { |
---|
167 | IHighlightingDefinition rh; |
---|
168 | if (highlightingsByExtension.TryGetValue(extension, out rh)) |
---|
169 | return rh; |
---|
170 | else |
---|
171 | return null; |
---|
172 | } |
---|
173 | } |
---|
174 | |
---|
175 | /// <summary> |
---|
176 | /// Registers a highlighting definition. |
---|
177 | /// </summary> |
---|
178 | /// <param name="name">The name to register the definition with.</param> |
---|
179 | /// <param name="extensions">The file extensions to register the definition for.</param> |
---|
180 | /// <param name="highlighting">The highlighting definition.</param> |
---|
181 | public void RegisterHighlighting(string name, string[] extensions, IHighlightingDefinition highlighting) |
---|
182 | { |
---|
183 | if (highlighting == null) |
---|
184 | throw new ArgumentNullException("highlighting"); |
---|
185 | |
---|
186 | lock (lockObj) { |
---|
187 | allHighlightings.Add(highlighting); |
---|
188 | if (name != null) { |
---|
189 | highlightingsByName[name] = highlighting; |
---|
190 | } |
---|
191 | if (extensions != null) { |
---|
192 | foreach (string ext in extensions) { |
---|
193 | highlightingsByExtension[ext] = highlighting; |
---|
194 | } |
---|
195 | } |
---|
196 | } |
---|
197 | } |
---|
198 | |
---|
199 | /// <summary> |
---|
200 | /// Registers a highlighting definition. |
---|
201 | /// </summary> |
---|
202 | /// <param name="name">The name to register the definition with.</param> |
---|
203 | /// <param name="extensions">The file extensions to register the definition for.</param> |
---|
204 | /// <param name="lazyLoadedHighlighting">A function that loads the highlighting definition.</param> |
---|
205 | public void RegisterHighlighting(string name, string[] extensions, Func<IHighlightingDefinition> lazyLoadedHighlighting) |
---|
206 | { |
---|
207 | if (lazyLoadedHighlighting == null) |
---|
208 | throw new ArgumentNullException("lazyLoadedHighlighting"); |
---|
209 | RegisterHighlighting(name, extensions, new DelayLoadedHighlightingDefinition(name, lazyLoadedHighlighting)); |
---|
210 | } |
---|
211 | |
---|
212 | /// <summary> |
---|
213 | /// Gets the default HighlightingManager instance. |
---|
214 | /// The default HighlightingManager comes with built-in highlightings. |
---|
215 | /// </summary> |
---|
216 | public static HighlightingManager Instance { |
---|
217 | get { |
---|
218 | return DefaultHighlightingManager.Instance; |
---|
219 | } |
---|
220 | } |
---|
221 | |
---|
222 | internal sealed class DefaultHighlightingManager : HighlightingManager |
---|
223 | { |
---|
224 | public new static readonly DefaultHighlightingManager Instance = new DefaultHighlightingManager(); |
---|
225 | |
---|
226 | public DefaultHighlightingManager() |
---|
227 | { |
---|
228 | Resources.RegisterBuiltInHighlightings(this); |
---|
229 | } |
---|
230 | |
---|
231 | // Registering a built-in highlighting |
---|
232 | internal void RegisterHighlighting(string name, string[] extensions, string resourceName) |
---|
233 | { |
---|
234 | try { |
---|
235 | #if DEBUG |
---|
236 | // don't use lazy-loading in debug builds, show errors immediately |
---|
237 | Xshd.XshdSyntaxDefinition xshd; |
---|
238 | using (Stream s = Resources.OpenStream(resourceName)) { |
---|
239 | using (XmlTextReader reader = new XmlTextReader(s)) { |
---|
240 | xshd = Xshd.HighlightingLoader.LoadXshd(reader, false); |
---|
241 | } |
---|
242 | } |
---|
243 | Debug.Assert(name == xshd.Name); |
---|
244 | if (extensions != null) |
---|
245 | Debug.Assert(System.Linq.Enumerable.SequenceEqual(extensions, xshd.Extensions)); |
---|
246 | else |
---|
247 | Debug.Assert(xshd.Extensions.Count == 0); |
---|
248 | |
---|
249 | // round-trip xshd: |
---|
250 | // string resourceFileName = Path.Combine(Path.GetTempPath(), resourceName); |
---|
251 | // using (XmlTextWriter writer = new XmlTextWriter(resourceFileName, System.Text.Encoding.UTF8)) { |
---|
252 | // writer.Formatting = Formatting.Indented; |
---|
253 | // new Xshd.SaveXshdVisitor(writer).WriteDefinition(xshd); |
---|
254 | // } |
---|
255 | // using (FileStream fs = File.Create(resourceFileName + ".bin")) { |
---|
256 | // new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(fs, xshd); |
---|
257 | // } |
---|
258 | // using (FileStream fs = File.Create(resourceFileName + ".compiled")) { |
---|
259 | // new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(fs, Xshd.HighlightingLoader.Load(xshd, this)); |
---|
260 | // } |
---|
261 | |
---|
262 | RegisterHighlighting(name, extensions, Xshd.HighlightingLoader.Load(xshd, this)); |
---|
263 | #else |
---|
264 | RegisterHighlighting(name, extensions, LoadHighlighting(resourceName)); |
---|
265 | #endif |
---|
266 | } catch (HighlightingDefinitionInvalidException ex) { |
---|
267 | throw new InvalidOperationException("The built-in highlighting '" + name + "' is invalid.", ex); |
---|
268 | } |
---|
269 | } |
---|
270 | |
---|
271 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", |
---|
272 | Justification = "LoadHighlighting is used only in release builds")] |
---|
273 | Func<IHighlightingDefinition> LoadHighlighting(string resourceName) |
---|
274 | { |
---|
275 | Func<IHighlightingDefinition> func = delegate { |
---|
276 | Xshd.XshdSyntaxDefinition xshd; |
---|
277 | using (Stream s = Resources.OpenStream(resourceName)) { |
---|
278 | using (XmlTextReader reader = new XmlTextReader(s)) { |
---|
279 | // in release builds, skip validating the built-in highlightings |
---|
280 | xshd = Xshd.HighlightingLoader.LoadXshd(reader, true); |
---|
281 | } |
---|
282 | } |
---|
283 | return Xshd.HighlightingLoader.Load(xshd, this); |
---|
284 | }; |
---|
285 | return func; |
---|
286 | } |
---|
287 | } |
---|
288 | } |
---|
289 | } |
---|