PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/Mode.java

#
Java | 524 lines | 294 code | 50 blank | 180 comment | 73 complexity | 8c868e8cdc89804334e8c8d6fcc18d8b MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * Mode.java - jEdit editing mode
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1998, 1999, 2000 Slava Pestov
  7. * Copyright (C) 1999 mike dillon
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit;
  24. //{{{ Imports
  25. import java.lang.reflect.Method;
  26. import java.util.Hashtable;
  27. import java.util.Collections;
  28. import java.util.LinkedList;
  29. import java.util.List;
  30. import java.util.Map;
  31. import java.util.regex.Pattern;
  32. import java.util.regex.PatternSyntaxException;
  33. import org.gjt.sp.jedit.indent.DeepIndentRule;
  34. import org.gjt.sp.jedit.indent.IndentRule;
  35. import org.gjt.sp.jedit.indent.IndentRuleFactory;
  36. import org.gjt.sp.jedit.indent.WhitespaceRule;
  37. import org.gjt.sp.jedit.syntax.TokenMarker;
  38. import org.gjt.sp.jedit.syntax.ModeProvider;
  39. import org.gjt.sp.util.Log;
  40. import org.gjt.sp.util.StandardUtilities;
  41. //}}}
  42. /**
  43. * An edit mode defines specific settings for editing some type of file.
  44. * One instance of this class is created for each supported edit mode.
  45. *
  46. * @author Slava Pestov
  47. * @version $Id: Mode.java 20007 2011-09-24 00:49:35Z Vampire0 $
  48. */
  49. public class Mode
  50. {
  51. //{{{ Mode constructor
  52. /**
  53. * Creates a new edit mode.
  54. *
  55. * @param name The name used in mode listings and to query mode
  56. * properties
  57. * @see #getProperty(String)
  58. */
  59. public Mode(String name)
  60. {
  61. this.name = name;
  62. this.ignoreWhitespace = true;
  63. props = new Hashtable<String, Object>();
  64. } //}}}
  65. //{{{ init() method
  66. /**
  67. * Initializes the edit mode. Should be called after all properties
  68. * are loaded and set.
  69. */
  70. public void init()
  71. {
  72. try
  73. {
  74. filepathRE = null;
  75. String filenameGlob = (String)getProperty("filenameGlob");
  76. if(filenameGlob != null && filenameGlob.length() != 0)
  77. {
  78. // translate glob to regex
  79. String filepathRE = StandardUtilities.globToRE(filenameGlob);
  80. // if glob includes a path separator (both are supported as
  81. // users can supply them in the GUI and thus will copy
  82. // Windows paths in there)
  83. if (filepathRE.contains("/") || filepathRE.contains("\\\\"))
  84. {
  85. // replace path separators by both separator possibilities in the regex
  86. filepathRE = filepathRE.replaceAll("/|\\\\\\\\", "[/\\\\\\\\]");
  87. } else {
  88. // glob is for a filename without path, prepend the regex with
  89. // an optional path prefix to be able to match against full paths
  90. filepathRE = String.format("(?:.*[/\\\\])?%s", filepathRE);
  91. }
  92. this.filepathRE = Pattern.compile(filepathRE, Pattern.CASE_INSENSITIVE);
  93. }
  94. firstlineRE = null;
  95. String firstlineGlob = (String)getProperty("firstlineGlob");
  96. if(firstlineGlob != null && firstlineGlob.length() != 0)
  97. {
  98. firstlineRE = Pattern.compile(StandardUtilities.globToRE(firstlineGlob),
  99. Pattern.CASE_INSENSITIVE);
  100. }
  101. }
  102. catch(PatternSyntaxException re)
  103. {
  104. Log.log(Log.ERROR,this,"Invalid filename/firstline"
  105. + " globs in mode " + name);
  106. Log.log(Log.ERROR,this,re);
  107. }
  108. // Fix for this bug:
  109. // -- Put a mode into the user dir with the same name as one
  110. // on the system dir.
  111. // -- Reload edit modes.
  112. // -- Old mode from system dir still used for highlighting
  113. // until jEdit restart.
  114. marker = null;
  115. } //}}}
  116. //{{{ getTokenMarker() method
  117. /**
  118. * Returns the token marker for this mode.
  119. */
  120. public TokenMarker getTokenMarker()
  121. {
  122. loadIfNecessary();
  123. return marker;
  124. } //}}}
  125. //{{{ setTokenMarker() method
  126. /**
  127. * Sets the token marker for this mode.
  128. * @param marker The new token marker
  129. */
  130. public void setTokenMarker(TokenMarker marker)
  131. {
  132. this.marker = marker;
  133. } //}}}
  134. //{{{ loadIfNecessary() method
  135. /**
  136. * Loads the mode from disk if it hasn't been loaded already.
  137. * @since jEdit 2.5pre3
  138. */
  139. public void loadIfNecessary()
  140. {
  141. if(marker == null)
  142. {
  143. ModeProvider.instance.loadMode(this);
  144. if (marker == null)
  145. Log.log(Log.ERROR, this, "Mode not correctly loaded, token marker is still null");
  146. }
  147. } //}}}
  148. //{{{ getProperty() method
  149. /**
  150. * Returns a mode property.
  151. * @param key The property name
  152. *
  153. * @since jEdit 2.2pre1
  154. */
  155. public Object getProperty(String key)
  156. {
  157. Object value = props.get(key);
  158. if(value != null)
  159. return value;
  160. return null;
  161. } //}}}
  162. //{{{ getBooleanProperty() method
  163. /**
  164. * Returns the value of a boolean property.
  165. * @param key The property name
  166. *
  167. * @since jEdit 2.5pre3
  168. */
  169. public boolean getBooleanProperty(String key)
  170. {
  171. Object value = getProperty(key);
  172. return StandardUtilities.getBoolean(value, false);
  173. } //}}}
  174. //{{{ setProperty() method
  175. /**
  176. * Sets a mode property.
  177. * @param key The property name
  178. * @param value The property value
  179. */
  180. public void setProperty(String key, Object value)
  181. {
  182. props.put(key,value);
  183. } //}}}
  184. //{{{ unsetProperty() method
  185. /**
  186. * Unsets a mode property.
  187. * @param key The property name
  188. * @since jEdit 3.2pre3
  189. */
  190. public void unsetProperty(String key)
  191. {
  192. props.remove(key);
  193. } //}}}
  194. //{{{ setProperties() method
  195. /**
  196. * Should only be called by <code>XModeHandler</code>.
  197. * @since jEdit 4.0pre3
  198. */
  199. public void setProperties(Map props)
  200. {
  201. if(props == null)
  202. props = new Hashtable<String, Object>();
  203. ignoreWhitespace = !"false".equalsIgnoreCase(
  204. (String)props.get("ignoreWhitespace"));
  205. // need to carry over file name and first line globs because they are
  206. // not given to us by the XMode handler, but instead are filled in by
  207. // the catalog loader.
  208. String filenameGlob = (String)this.props.get("filenameGlob");
  209. String firstlineGlob = (String)this.props.get("firstlineGlob");
  210. String filename = (String)this.props.get("file");
  211. this.props = props;
  212. if(filenameGlob != null)
  213. props.put("filenameGlob",filenameGlob);
  214. if(firstlineGlob != null)
  215. props.put("firstlineGlob",firstlineGlob);
  216. if(filename != null)
  217. props.put("file",filename);
  218. } //}}}
  219. //{{{ accept() method
  220. /**
  221. * Returns true if the edit mode is suitable for editing the specified
  222. * file. The buffer name and first line is checked against the
  223. * file name and first line globs, respectively.
  224. * @param fileName The buffer's name, can be {@code null}
  225. * @param firstLine The first line of the buffer
  226. *
  227. * @since jEdit 3.2pre3
  228. */
  229. public boolean accept(String fileName, String firstLine)
  230. {
  231. return accept(null, fileName, firstLine);
  232. } //}}}
  233. //{{{ accept() method
  234. /**
  235. * Returns true if the edit mode is suitable for editing the specified
  236. * file. The buffer name and first line is checked against the
  237. * file name and first line globs, respectively.
  238. * @param filePath The buffer's path, can be {@code null}
  239. * @param fileName The buffer's name, can be {@code null}
  240. * @param firstLine The first line of the buffer
  241. *
  242. * @since jEdit 4.5pre1
  243. */
  244. public boolean accept(String filePath, String fileName, String firstLine)
  245. {
  246. return acceptIdentical(filePath, fileName)
  247. || acceptFile(filePath, fileName)
  248. || acceptFirstLine(firstLine);
  249. } //}}}
  250. //{{{ acceptFilename() method
  251. /**
  252. * Returns true if the buffer name matches the file name glob.
  253. * @param fileName The buffer's name, can be {@code null}
  254. * @return true if the file name matches the file name glob.
  255. * @since jEdit 4.3pre18
  256. * @deprecated use {@link #acceptFile(String, String)} instead
  257. */
  258. @Deprecated
  259. public boolean acceptFilename(String fileName)
  260. {
  261. return acceptFile(null, fileName);
  262. } //}}}
  263. //{{{ acceptFile() method
  264. /**
  265. * Returns true if the buffer's name or path matches the file name glob.
  266. * @param filePath The buffer's path, can be {@code null}
  267. * @param fileName The buffer's name, can be {@code null}
  268. * @return true if the file path or name matches the file name glob.
  269. * @since jEdit 4.5pre1
  270. */
  271. public boolean acceptFile(String filePath, String fileName)
  272. {
  273. return filepathRE != null
  274. && (((filePath != null) && filepathRE.matcher(filePath).matches())
  275. || ((fileName != null) && filepathRE.matcher(fileName).matches()));
  276. } //}}}
  277. //{{{ acceptFilenameIdentical() method
  278. /**
  279. * Returns true if the buffer name is identical to the file name glob.
  280. * This works only for regular expressions that only represent themselves,
  281. * i.e. without any meta-characters.
  282. * @param fileName The buffer's name, can be {@code null}
  283. * @return true if the file name matches the file name glob.
  284. * @since jEdit 4.4pre1
  285. */
  286. public boolean acceptFilenameIdentical(String fileName)
  287. {
  288. return acceptIdentical(null, fileName);
  289. } //}}}
  290. //{{{ acceptIdentical() method
  291. /**
  292. * Returns true if the buffer path or name is identical to the file name glob.
  293. * This works only for regular expressions that only represent themselves,
  294. * i.e. without any meta-characters.
  295. * @param filePath The buffer's path, can be {@code null}
  296. * @param fileName The buffer's name, can be {@code null}
  297. * @return true if the file name matches the file name glob.
  298. * @since jEdit 4.5pre1
  299. */
  300. public boolean acceptIdentical(String filePath, String fileName)
  301. {
  302. return ((filePath != null) && filePath.equals(getProperty("filenameGlob"))
  303. && (filepathRE == null || filepathRE.matcher(filePath).matches()))
  304. || ((fileName != null) && fileName.equals(getProperty("filenameGlob"))
  305. && (filepathRE == null || filepathRE.matcher(fileName).matches()));
  306. } //}}}
  307. //{{{ acceptFirstLine() method
  308. /**
  309. * Returns true if the first line matches the first line glob.
  310. * @param firstLine The first line of the buffer
  311. * @return true if the first line matches the first line glob.
  312. * @since jEdit 4.3pre18
  313. */
  314. public boolean acceptFirstLine(String firstLine)
  315. {
  316. return firstlineRE != null && firstlineRE.matcher(firstLine).matches();
  317. } //}}}
  318. //{{{ getName() method
  319. /**
  320. * Returns the internal name of this edit mode.
  321. */
  322. public String getName()
  323. {
  324. return name;
  325. } //}}}
  326. //{{{ toString() method
  327. /**
  328. * Returns a string representation of this edit mode.
  329. */
  330. public String toString()
  331. {
  332. return name;
  333. } //}}}
  334. //{{{ getIgnoreWhitespace() method
  335. public boolean getIgnoreWhitespace()
  336. {
  337. return ignoreWhitespace;
  338. } //}}}
  339. //{{{ Indent rules
  340. public synchronized List<IndentRule> getIndentRules()
  341. {
  342. if (indentRules == null)
  343. {
  344. initIndentRules();
  345. }
  346. return indentRules;
  347. }
  348. public synchronized boolean isElectricKey(char ch)
  349. {
  350. if (electricKeys == null)
  351. {
  352. String[] props = {
  353. "indentOpenBrackets",
  354. "indentCloseBrackets",
  355. "electricKeys"
  356. };
  357. StringBuilder buf = new StringBuilder();
  358. for(int i = 0; i < props.length; i++)
  359. {
  360. String prop = (String) getProperty(props[i]);
  361. if (prop != null)
  362. buf.append(prop);
  363. }
  364. electricKeys = buf.toString();
  365. }
  366. return (electricKeys.indexOf(ch) >= 0);
  367. }
  368. private void initIndentRules()
  369. {
  370. List<IndentRule> rules = new LinkedList<IndentRule>();
  371. String[] regexpProps = {
  372. "indentNextLine",
  373. "indentNextLines"
  374. };
  375. for(int i = 0; i < regexpProps.length; i++)
  376. {
  377. IndentRule rule = createRegexpIndentRule(regexpProps[i]);
  378. if(rule != null)
  379. rules.add(rule);
  380. }
  381. String[] bracketProps = {
  382. "indentOpenBracket",
  383. "indentCloseBracket",
  384. "unalignedOpenBracket",
  385. "unalignedCloseBracket",
  386. };
  387. for(int i = 0; i < bracketProps.length; i++)
  388. {
  389. createBracketIndentRules(bracketProps[i], rules);
  390. }
  391. String[] finalProps = {
  392. "unindentThisLine",
  393. "unindentNextLines"
  394. };
  395. for(int i = 0; i < finalProps.length; i++)
  396. {
  397. IndentRule rule = createRegexpIndentRule(finalProps[i]);
  398. if(rule != null)
  399. rules.add(rule);
  400. }
  401. if (getBooleanProperty("deepIndent"))
  402. {
  403. String unalignedOpenBrackets = (String) getProperty("unalignedOpenBrackets");
  404. if (unalignedOpenBrackets != null)
  405. {
  406. for (int i = 0 ; i < unalignedOpenBrackets.length();i++)
  407. {
  408. char openChar = unalignedOpenBrackets.charAt(i);
  409. char closeChar = TextUtilities.getComplementaryBracket(openChar, null);
  410. if (closeChar != '\0')
  411. rules.add(new DeepIndentRule(openChar, closeChar));
  412. }
  413. }
  414. }
  415. if (!getIgnoreWhitespace())
  416. rules.add(new WhitespaceRule());
  417. indentRules = Collections.unmodifiableList(rules);
  418. }
  419. private IndentRule createRegexpIndentRule(String prop)
  420. {
  421. String value = (String) getProperty(prop);
  422. try
  423. {
  424. if(value != null)
  425. {
  426. Method m = IndentRuleFactory.class.getMethod(
  427. prop,new Class[] { String.class });
  428. return (IndentRule)m.invoke(null, value);
  429. }
  430. }
  431. catch(Exception e)
  432. {
  433. Log.log(Log.ERROR,this,"Bad indent rule " + prop
  434. + '=' + value + ':');
  435. Log.log(Log.ERROR,this,e);
  436. }
  437. return null;
  438. }
  439. private void createBracketIndentRules(String prop,
  440. List<IndentRule> rules)
  441. {
  442. String value = (String) getProperty(prop + 's');
  443. try
  444. {
  445. if(value != null)
  446. {
  447. for(int i = 0; i < value.length(); i++)
  448. {
  449. char ch = value.charAt(i);
  450. Method m = IndentRuleFactory.class.getMethod(
  451. prop,new Class[] { char.class });
  452. rules.add((IndentRule) m.invoke(null, ch));
  453. }
  454. }
  455. }
  456. catch(Exception e)
  457. {
  458. Log.log(Log.ERROR,this,"Bad indent rule " + prop
  459. + '=' + value + ':');
  460. Log.log(Log.ERROR,this,e);
  461. }
  462. }
  463. //}}}
  464. //{{{ Private members
  465. protected String name;
  466. protected Map<String, Object> props;
  467. private Pattern firstlineRE;
  468. private Pattern filepathRE;
  469. protected TokenMarker marker;
  470. private List<IndentRule> indentRules;
  471. private String electricKeys;
  472. private boolean ignoreWhitespace;
  473. //}}}
  474. }