Projekt na (niejeden) weekend: Netbeans TOML plugin - zwijanie kodu
Kolejny etap obsługi plików Tom's Obvious, Minimal Language.:
Mam już ropoznawanie typu pliku oraz [kolorowanie składni](/2020-03-01-netbeans-toml-plugin-syntax-color//, a nawet breadcrums
Pora na "zwijanie" kodu.
Zwijanie
Funkcjonalność zwijania, jak już wspominałem wcześniej
należy do klasy implementującej metodę folds
z interfejsu StructureScanner.
Ponieważ strukturę dokumentu mamy już rozpoznaną, jedyne czego nam potrzeba, to rekursywnie przejść przez nią i
każdy napotkany element typu tabela, zamienić na "zwijalny" obszar:
@Override
public Map<String, List<OffsetRange>> folds(ParserResult parserResult) {
if (parserResult == null) {
return Collections.emptyMap();
}
if (!(parserResult instanceof TomlParserResult)) {
Logger.getLogger(TomlStructureScanner.class.getName()).log(Level.WARNING, "parser result of unexpected type "+parserResult.getClass().getName());
return Collections.emptyMap();
}
TomlParserResult tomlResult = (TomlParserResult) parserResult;
List<AbstractTomlStructureItem> listOfRoots = tomlResult.getStructure();
List<OffsetRange> ranges = listOfRoots.stream()
.flatMap(AbstractTomlStructureItem::flattened)
.filter(item -> item instanceof TableStructureItem)
.map(item -> new OffsetRange((int) item.getPosition(), (int) item.getEndPosition()))
.collect(Collectors.toList());
return Collections.singletonMap("tags", ranges);
}
flattened
to nowa metoda w AbstractTomlStructureItem
zapewniająca łatwą obsługę rekurencji: zwraca nowy stream
składający się z aktualnej instancji i streamu będącego wynikiem wywołań flattened
na dzieciach:
public Stream<? extends AbstractTomlStructureItem> flattened() {
return Stream.concat(
Stream.of(this),
children.stream().flatMap(AbstractTomlStructureItem::flattened));
}
i... widać linie pokazujące "zwijalne" rejony:
działa też zwijanie:
Czyli działa, ale... (jak zwykle jest jakieś ale), nie wygląda to aż tak dobrze jak np. w przypadku xml (czyli nie widać "co" faktycznie zwinęliśmy):
Konfiguracja
Rozwiązaniem powyższego problemu jest dodanie własnej konfiguracji zwijań.
Własny typ zwijania
Dla każdego typu mime (w tym naszego application/toml
) każda wtyczka może sobie zdefiniować jakie typy "zwinięć"
będą obsługiwane. Trzeba zaimplementować interfejs FoldTypeProvider
i zarejestrować go dla wskazanego typu mime:
@MimeRegistration(mimeType = "application/toml", service = FoldTypeProvider.class)
public class TomlFoldTypeProvider implements FoldTypeProvider {
@NbBundle.Messages("FoldType_TomlTables=Tables")
public static final FoldType TOML_TABLE = FoldType.TAG.override(Bundle.FoldType_TomlTables(),
new org.netbeans.api.editor.fold.FoldTemplate(1, 1, FoldTemplate.CONTENT_PLACEHOLDER)); // NOI18N
List<FoldType> foldTypes = Collections.singletonList(TOML_TABLE);
@Override
public Collection getValues(Class type) {
return foldTypes;
}
@Override
public boolean inheritable() {
return false;
}
}
po kolei:
@MimeRegistration
czyli rejestrujemy daną klasę jakoservice
(określonego typu) dla podanego typu mime.@Messages
, które już wcześniej wykorzystywałem do definicji tekstów, które mogą być tłumaczone na inne języki.
public static final FoldType TOML_TABLE = FoldType.TAG.override(Bundle.FoldType_TomlTables(),
new org.netbeans.api.editor.fold.FoldTemplate(1, 1, FoldTemplate.CONTENT_PLACEHOLDER)); // NOI18N
- definicja typu
(
FoldType
) zwijania. W moim przypadku nadpisuję istniejący typTAGS
(bo już z niego skorzystałem wStructureScanner
). UżywamFoldTemplate.CONTENT_PLACEHOLDER
) jako wzorca, ponieważ, jeśli dodamy potem zwracanie własnego tekstu, to zawartość tego szablonu zostanie podmieniona na "nasz" tekst.
pozostałe metody/pola chyba nie wymagają objaśnień.
klasa wymaga oczywiście nowych wpisów w pom.xml
do kompilacji:
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-netbeans-modules-editor-fold</artifactId>
<version>${netbeans.version}</version>
</dependency>
Zarejestrowany typ pojawia się już w konfiguracji edytora:
Ponieważ nadpisaliśmy istniejący typ, to domyślne ustawienia zwijania dla Tags
będą również obowiązywać dla naszego typu.
Wyświetlana zawartość po zwinięciu
Domyślnie po zwinięciu obszaru wyświetlana jest zawartość ustawiona jako szablon w definicji typu zwinięcia.
Jeśli chcemy wyświetlić tam jakąś inną wartość (podobnie jak np. w przypadku xml), potrzeba dla każdego obsługiwanego typu
zwinięcia zaimplementować interfejs ContentReader
oraz Factory
które będzie tworzyć instancje tych ContentReader
:
public class TomlFoldContentReader implements ContentReader {
@Override
public CharSequence read(Document document, Fold fold, FoldTemplate foldTemplate) throws BadLocationException {
int lineEnd = LineDocumentUtils.getLineEnd((LineDocument) document, fold.getStartOffset());
String text = document.getText(fold.getStartOffset(), lineEnd - fold.getStartOffset());
int indexOf = text.indexOf(']');
if (indexOf > -1) {
return text.substring(0, indexOf + 1);
}
return null;
}
@MimeRegistration(mimeType = "application/toml", service = ContentReader.Factory.class)
public static class TomlFoldContentReaderFactory implements ContentReader.Factory {
@Override
public ContentReader createReader(FoldType ft) {
if (ft == TomlFoldTypeProvider.TOML_TABLE) {
return new TomlFoldContentReader();
}
return null;
}
}
}
read
czyli metoda, która zwraca to co powinno pokazać się zamiast...
- jak widać znajduję najbliższy znak końca tabeli i cały tekst od początku zwinięcia (czyli nazwę tabeli) zwracam jako to co ma się pokazać.TomlFoldContentReaderFactory
- czyli spięcie naszegoTomlFoldContentReader
ze zdefiniowanym wcześniej typem zwijaniaTomlFoldTypeProvider.TOML_TABLE
...i kolejna zależność w pom.xml
:
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-netbeans-modules-editor-document</artifactId>
<version>${netbeans.version}</version>
</dependency>
Podsumowanie
Efekt końcowy:
Repozytorium z aktualnym kodem: https://gitlab.com/ihsahn/netbeans-toml
Literatura
- Tom's Obvious, Minimal Language. oficjalne repo Toml
- Rich Client Programming: Plugging Into the NetBeans Platform książka o programowaniu z wykorzystaniem platformy Netbeans
- NetBeans api javadoc