@@ -1426,17 +1426,31 @@ protected static SyntaxHighlighter build(List<Path> syntaxFiles, String file, St
1426
1426
, boolean ignoreErrors ) {
1427
1427
SyntaxHighlighter out = new SyntaxHighlighter ();
1428
1428
List <HighlightRule > defaultRules = new ArrayList <>();
1429
+ Map <String , String > colorTheme = new HashMap <>();
1429
1430
try {
1430
1431
if (syntaxName == null || (syntaxName != null && !syntaxName .equals ("none" ))) {
1431
1432
for (Path p : syntaxFiles ) {
1432
1433
try {
1433
- NanorcParser parser = new NanorcParser (p , syntaxName , file );
1434
- parser .parse ();
1435
- if (parser .matches ()) {
1436
- out .addRules (parser .getHighlightRules ());
1437
- return out ;
1438
- } else if (parser .isDefault ()) {
1439
- defaultRules .addAll (parser .getHighlightRules ());
1434
+ if (colorTheme .isEmpty () && p .getFileName ().toString ().endsWith (".nanorctheme" )) {
1435
+ try (BufferedReader reader = new BufferedReader (new FileReader (p .toFile ()))) {
1436
+ String line ;
1437
+ while ((line = reader .readLine ()) != null ) {
1438
+ line = line .trim ();
1439
+ if (line .length () > 0 && !line .startsWith ("#" )) {
1440
+ List <String > parts = Arrays .asList (line .split ("\\ s+" , 2 ));
1441
+ colorTheme .put (parts .get (0 ), parts .get (1 ));
1442
+ }
1443
+ }
1444
+ }
1445
+ } else {
1446
+ NanorcParser parser = new NanorcParser (p , syntaxName , file , colorTheme );
1447
+ parser .parse ();
1448
+ if (parser .matches ()) {
1449
+ out .addRules (parser .getHighlightRules ());
1450
+ return out ;
1451
+ } else if (parser .isDefault ()) {
1452
+ defaultRules .addAll (parser .getHighlightRules ());
1453
+ }
1440
1454
}
1441
1455
} catch (IOException e ) {
1442
1456
// ignore
@@ -1464,8 +1478,8 @@ public static SyntaxHighlighter build(Path nanorc, String syntaxName) {
1464
1478
List <Path > syntaxFiles = new ArrayList <>();
1465
1479
try {
1466
1480
try (BufferedReader reader = new BufferedReader (new FileReader (nanorc .toFile ()))) {
1467
- String line = reader . readLine () ;
1468
- while (line != null ) {
1481
+ String line ;
1482
+ while (( line = reader . readLine ()) != null ) {
1469
1483
line = line .trim ();
1470
1484
if (line .length () > 0 && !line .startsWith ("#" )) {
1471
1485
List <String > parts = Parser .split (line );
@@ -1481,9 +1495,19 @@ public static SyntaxHighlighter build(Path nanorc, String syntaxName) {
1481
1495
} else {
1482
1496
syntaxFiles .add (Paths .get (parts .get (1 )));
1483
1497
}
1498
+ } else if (parts .get (0 ).equals ("theme" )) {
1499
+ if (parts .get (1 ).contains ("*" ) || parts .get (1 ).contains ("?" )) {
1500
+ PathMatcher pathMatcher = FileSystems .getDefault ().getPathMatcher ("glob:" + parts .get (1 ));
1501
+ Optional <Path > theme = Files .find (Paths .get (new File (parts .get (1 )).getParent ()), Integer .MAX_VALUE , (path , f ) -> pathMatcher .matches (path ))
1502
+ .findFirst ();
1503
+ if (theme .isPresent ()) {
1504
+ syntaxFiles .add (0 , theme .get ());
1505
+ }
1506
+ } else {
1507
+ syntaxFiles .add (0 , Paths .get (parts .get (1 )));
1508
+ }
1484
1509
}
1485
1510
}
1486
- line = reader .readLine ();
1487
1511
}
1488
1512
}
1489
1513
out = build (syntaxFiles , null , syntaxName );
@@ -1678,11 +1702,13 @@ private static class NanorcParser {
1678
1702
private final String target ;
1679
1703
private final List <HighlightRule > highlightRules = new ArrayList <>();
1680
1704
private final BufferedReader reader ;
1705
+ private Map <String , String > colorTheme = new HashMap <>();
1681
1706
private boolean matches = false ;
1682
1707
private String syntaxName = "unknown" ;
1683
1708
1684
- public NanorcParser (Path file , String name , String target ) throws IOException {
1709
+ public NanorcParser (Path file , String name , String target , Map < String , String > colorTheme ) throws IOException {
1685
1710
this (new Source .PathSource (file , null ).read (), name , target );
1711
+ this .colorTheme = colorTheme ;
1686
1712
}
1687
1713
1688
1714
public NanorcParser (InputStream in , String name , String target ) {
@@ -1698,21 +1724,7 @@ public void parse() throws IOException {
1698
1724
idx ++;
1699
1725
line = line .trim ();
1700
1726
if (line .length () > 0 && !line .startsWith ("#" )) {
1701
- line = line .replaceAll ("\\ \\ <" , "\\ \\ b" )
1702
- .replaceAll ("\\ \\ >" , "\\ \\ b" )
1703
- .replaceAll ("\\ [:alnum:]" , "\\ \\ p{Alnum}" )
1704
- .replaceAll ("\\ [:alpha:]" , "\\ \\ p{Alpha}" )
1705
- .replaceAll ("\\ [:blank:]" , "\\ \\ p{Blank}" )
1706
- .replaceAll ("\\ [:cntrl:]" , "\\ \\ p{Cntrl}" )
1707
- .replaceAll ("\\ [:digit:]" , "\\ \\ p{Digit}" )
1708
- .replaceAll ("\\ [:graph:]" , "\\ \\ p{Graph}" )
1709
- .replaceAll ("\\ [:lower:]" , "\\ \\ p{Lower}" )
1710
- .replaceAll ("\\ [:print:]" , "\\ \\ p{Print}" )
1711
- .replaceAll ("\\ [:punct:]" , "\\ \\ p{Punct}" )
1712
- .replaceAll ("\\ [:space:]" , "\\ \\ s" )
1713
- .replaceAll ("\\ [:upper:]" , "\\ \\ p{Upper}" )
1714
- .replaceAll ("\\ [:xdigit:]" , "\\ \\ p{XDigit}" );
1715
- List <String > parts = Parser .split (line );
1727
+ List <String > parts = Parser .split (fixRegexes (line ));
1716
1728
if (parts .get (0 ).equals ("syntax" )) {
1717
1729
syntaxName = parts .get (1 );
1718
1730
List <Pattern > filePatterns = new ArrayList <>();
@@ -1726,7 +1738,7 @@ public void parse() throws IOException {
1726
1738
for (int i = 2 ; i < parts .size (); i ++) {
1727
1739
filePatterns .add (Pattern .compile (parts .get (i )));
1728
1740
}
1729
- for (Pattern p : filePatterns ) {
1741
+ for (Pattern p : filePatterns ) {
1730
1742
if (p .matcher (target ).find ()) {
1731
1743
matches = true ;
1732
1744
break ;
@@ -1738,16 +1750,81 @@ public void parse() throws IOException {
1738
1750
} else {
1739
1751
matches = true ;
1740
1752
}
1741
- } else if (parts .get (0 ).equals ("color" )) {
1742
- addHighlightRule (syntaxName + idx , parts , false );
1743
- } else if (parts .get (0 ).equals ("icolor" )) {
1744
- addHighlightRule (syntaxName + idx , parts , true );
1753
+ } else if (!addHighlightRule (parts , idx ) && parts .get (0 ).matches ("\\ +[A-Z_]+" )) {
1754
+ String key = themeKey (parts .get (0 ));
1755
+ if (colorTheme .containsKey (key )) {
1756
+ for (String l : colorTheme .get (key ).split ("\\ \\ n" )) {
1757
+ idx ++;
1758
+ addHighlightRule (Parser .split (fixRegexes (l )), idx );
1759
+ }
1760
+ } else {
1761
+ Log .warn ("Unknown token type: " , key );
1762
+ }
1745
1763
}
1746
1764
}
1747
1765
}
1748
1766
reader .close ();
1749
1767
}
1750
1768
1769
+ private String fixRegexes (String line ) {
1770
+ return line .replaceAll ("\\ \\ <" , "\\ \\ b" )
1771
+ .replaceAll ("\\ \\ >" , "\\ \\ b" )
1772
+ .replaceAll ("\\ [:alnum:]" , "\\ \\ p{Alnum}" )
1773
+ .replaceAll ("\\ [:alpha:]" , "\\ \\ p{Alpha}" )
1774
+ .replaceAll ("\\ [:blank:]" , "\\ \\ p{Blank}" )
1775
+ .replaceAll ("\\ [:cntrl:]" , "\\ \\ p{Cntrl}" )
1776
+ .replaceAll ("\\ [:digit:]" , "\\ \\ p{Digit}" )
1777
+ .replaceAll ("\\ [:graph:]" , "\\ \\ p{Graph}" )
1778
+ .replaceAll ("\\ [:lower:]" , "\\ \\ p{Lower}" )
1779
+ .replaceAll ("\\ [:print:]" , "\\ \\ p{Print}" )
1780
+ .replaceAll ("\\ [:punct:]" , "\\ \\ p{Punct}" )
1781
+ .replaceAll ("\\ [:space:]" , "\\ \\ s" )
1782
+ .replaceAll ("\\ [:upper:]" , "\\ \\ p{Upper}" )
1783
+ .replaceAll ("\\ [:xdigit:]" , "\\ \\ p{XDigit}" );
1784
+ }
1785
+
1786
+ private boolean addHighlightRule (List <String > parts , int idx ) {
1787
+ boolean out = true ;
1788
+ if (parts .get (0 ).equals ("color" )) {
1789
+ addHighlightRule (syntaxName + idx , parts , false );
1790
+ } else if (parts .get (0 ).equals ("icolor" )) {
1791
+ addHighlightRule (syntaxName + idx , parts , true );
1792
+ } else if (parts .get (0 ).matches ("[A-Z_]+[:]?" )) {
1793
+ String key = themeKey (parts .get (0 ));
1794
+ if (colorTheme .containsKey (key )) {
1795
+ parts .set (0 , "color" );
1796
+ parts .add (1 , colorTheme .get (key ));
1797
+ addHighlightRule (syntaxName + idx , parts , false );
1798
+ } else {
1799
+ Log .warn ("Unknown token type: " , key );
1800
+ }
1801
+ } else if (parts .get (0 ).matches ("~[A-Z_]+[:]?" )) {
1802
+ String key = themeKey (parts .get (0 ));
1803
+ if (colorTheme .containsKey (key )) {
1804
+ parts .set (0 , "icolor" );
1805
+ parts .add (1 , colorTheme .get (key ));
1806
+ addHighlightRule (syntaxName + idx , parts , true );
1807
+ } else {
1808
+ Log .warn ("Unknown token type: " , key );
1809
+ }
1810
+ } else {
1811
+ out = false ;
1812
+ }
1813
+ return out ;
1814
+ }
1815
+
1816
+ private String themeKey (String key ) {
1817
+ if (key .startsWith ("+" )) {
1818
+ return key ;
1819
+ } else {
1820
+ int keyEnd = key .endsWith (":" ) ? key .length () - 1 : key .length ();
1821
+ if (key .startsWith ("~" )) {
1822
+ return key .substring (1 , keyEnd );
1823
+ }
1824
+ return key .substring (0 , keyEnd );
1825
+ }
1826
+ }
1827
+
1751
1828
public boolean matches () {
1752
1829
return matches ;
1753
1830
}
@@ -2062,8 +2139,8 @@ public Nano(Terminal terminal, Path root, Options opts, ConfigurationPath config
2062
2139
2063
2140
private void parseConfig (Path file ) throws IOException {
2064
2141
try (BufferedReader reader = new BufferedReader (new FileReader (file .toFile ()))) {
2065
- String line = reader . readLine () ;
2066
- while (line != null ) {
2142
+ String line ;
2143
+ while (( line = reader . readLine ()) != null ) {
2067
2144
line = line .trim ();
2068
2145
if (line .length () > 0 && !line .startsWith ("#" )) {
2069
2146
List <String > parts = Parser .split (line );
@@ -2075,6 +2152,17 @@ private void parseConfig(Path file) throws IOException {
2075
2152
} else {
2076
2153
syntaxFiles .add (Paths .get (parts .get (1 )));
2077
2154
}
2155
+ } else if (parts .get (0 ).equals ("theme" )) {
2156
+ if (parts .get (1 ).contains ("*" ) || parts .get (1 ).contains ("?" )) {
2157
+ PathMatcher pathMatcher = FileSystems .getDefault ().getPathMatcher ("glob:" + parts .get (1 ));
2158
+ Optional <Path > theme = Files .find (Paths .get (new File (parts .get (1 )).getParent ()), Integer .MAX_VALUE , (path , f ) -> pathMatcher .matches (path ))
2159
+ .findFirst ();
2160
+ if (theme .isPresent ()) {
2161
+ syntaxFiles .add (0 , theme .get ());
2162
+ }
2163
+ } else {
2164
+ syntaxFiles .add (0 , Paths .get (parts .get (1 )));
2165
+ }
2078
2166
} else if (parts .size () == 2
2079
2167
&& (parts .get (0 ).equals ("set" ) || parts .get (0 ).equals ("unset" ))) {
2080
2168
String option = parts .get (1 );
@@ -2136,7 +2224,6 @@ private void parseConfig(Path file) throws IOException {
2136
2224
errorMessage = "Nano config: Bad configuration '" + line + "'" ;
2137
2225
}
2138
2226
}
2139
- line = reader .readLine ();
2140
2227
}
2141
2228
}
2142
2229
}
0 commit comments