This is an automated email from the ASF dual-hosted git repository.

lkishalmi pushed a commit to branch release122
in repository https://gitbox.apache.org/repos/asf/netbeans.git

commit af35c99def8e74b2cc7d47043e0c98e053ade829
Author: Jan Lahoda <jlah...@netbeans.org>
AuthorDate: Sat Oct 17 21:45:57 2020 +0200

    [NETBEANS-4910] Correcting open and close events sent from the LSP client 
to the LSP server.
---
 .../org/netbeans/api/editor/EditorRegistry.java    |   5 +
 .../editor/lib2/EditorApiPackageAccessor.java      |   3 +
 .../lsp/client/bindings/BreadcrumbsImpl.java       |   3 +
 .../TextDocumentSyncServerCapabilityHandler.java   | 141 ++++++---
 ...extDocumentSyncServerCapabilityHandlerTest.java | 349 +++++++++++++++++++++
 5 files changed, 457 insertions(+), 44 deletions(-)

diff --git a/ide/editor.lib2/src/org/netbeans/api/editor/EditorRegistry.java 
b/ide/editor.lib2/src/org/netbeans/api/editor/EditorRegistry.java
index ac3ea6c..8ca9bea 100644
--- a/ide/editor.lib2/src/org/netbeans/api/editor/EditorRegistry.java
+++ b/ide/editor.lib2/src/org/netbeans/api/editor/EditorRegistry.java
@@ -781,6 +781,11 @@ public final class EditorRegistry {
         }
 
         @Override
+        public void forceRelease(JTextComponent c) {
+            EditorRegistry.releasedByCloneableEditor(c);
+        }
+
+        @Override
         public void setIgnoredAncestorClass(Class ignoredAncestorClass) {
             EditorRegistry.setIgnoredAncestorClass(ignoredAncestorClass);
         }
diff --git 
a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorApiPackageAccessor.java
 
b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorApiPackageAccessor.java
index a1606a1..0ca1aee 100644
--- 
a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorApiPackageAccessor.java
+++ 
b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/EditorApiPackageAccessor.java
@@ -51,6 +51,9 @@ public abstract class EditorApiPackageAccessor {
     
     /** Register text component to registry. */
     public abstract void register(JTextComponent c);
+
+    /**Forcibly release from the registry - useful for tests.*/
+    public abstract void forceRelease(JTextComponent c);
     
     public abstract void setIgnoredAncestorClass(Class ignoredAncestorClass);
     
diff --git 
a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/BreadcrumbsImpl.java
 
b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/BreadcrumbsImpl.java
index a19f481..fe9841c 100644
--- 
a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/BreadcrumbsImpl.java
+++ 
b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/BreadcrumbsImpl.java
@@ -278,6 +278,9 @@ public class BreadcrumbsImpl implements BackgroundTask {
         }
 
         public static List<BreadcrumbsElement> create(BreadcrumbsElement 
parent, List<DocumentSymbol> symbols, FileObject file, Document doc) {
+            if (symbols == null) {
+                return Collections.emptyList();
+            }
             return symbols.stream()
                           .map(c -> create(parent, file, doc, c))
                           .filter(e -> e != null)
diff --git 
a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
 
b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
index 60e38d3..81d98c0 100644
--- 
a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
+++ 
b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandler.java
@@ -21,8 +21,10 @@ package org.netbeans.modules.lsp.client.bindings;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import javax.swing.SwingUtilities;
 import javax.swing.event.DocumentEvent;
@@ -32,10 +34,12 @@ import javax.swing.text.Document;
 import javax.swing.text.JTextComponent;
 import javax.swing.text.StyledDocument;
 import org.eclipse.lsp4j.DidChangeTextDocumentParams;
+import org.eclipse.lsp4j.DidCloseTextDocumentParams;
 import org.eclipse.lsp4j.DidOpenTextDocumentParams;
 import org.eclipse.lsp4j.Position;
 import org.eclipse.lsp4j.Range;
 import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
+import org.eclipse.lsp4j.TextDocumentIdentifier;
 import org.eclipse.lsp4j.TextDocumentItem;
 import org.eclipse.lsp4j.TextDocumentSyncKind;
 import org.eclipse.lsp4j.TextDocumentSyncOptions;
@@ -55,8 +59,7 @@ import org.openide.text.NbDocument;
 import org.openide.util.Exceptions;
 import org.openide.util.RequestProcessor;
 
-/** TODO: follow the synchronization options
- *  TODO: close
+/**
  *
  * @author lahvac
  */
@@ -79,15 +82,57 @@ public class TextDocumentSyncServerCapabilityHandler {
         lastOpened.addAll(currentOpened);
 
         for (JTextComponent opened : newOpened) {
-            FileObject file = 
NbEditorUtilities.getFileObject(opened.getDocument());
+            editorOpened(opened);
+        }
 
-            if (file == null)
-                continue; //ignore
+        for (JTextComponent closed : newClosed) {
+            editorClosed(closed);
+        }
+    }
 
-            Document doc = opened.getDocument();
+    private void ensureOpenedInServer(JTextComponent opened) {
+        FileObject file = 
NbEditorUtilities.getFileObject(opened.getDocument());
 
-            ensureOpenedInServer(opened);
+        if (file == null)
+            return; //ignore
+
+        Document doc = opened.getDocument();
+        ensureDidOpenSent(doc);
+    }
+
+    public static void refreshOpenedFilesInServers() {
+        SwingUtilities.invokeLater(() -> {
+            assert SwingUtilities.isEventDispatchThread();
+            for (JTextComponent c : EditorRegistry.componentList()) {
+                h.ensureOpenedInServer(c);
+            }
+        });
+    }
 
+    private static final TextDocumentSyncServerCapabilityHandler h = new 
TextDocumentSyncServerCapabilityHandler();
+    @OnStart
+    public static class Init implements Runnable {
+
+        @Override
+        public void run() {
+            EditorRegistry.addPropertyChangeListener(evt -> h.handleChange());
+            SwingUtilities.invokeLater(() -> h.handleChange());
+        }
+
+    }
+
+    private final Map<Document, Integer> openDocument2PanesCount = new 
HashMap<>();
+
+    private void documentOpened(Document doc) {
+        FileObject file = NbEditorUtilities.getFileObject(doc);
+
+        if (file == null)
+            return; //ignore
+
+        openDocument2PanesCount.computeIfAbsent(doc, d -> {
+            doc.putProperty(HyperlinkProviderImpl.class, true);
+            doc.putProperty(TextDocumentSyncServerCapabilityHandler.class, 
true);
+            ensureDidOpenSent(doc);
             doc.addDocumentListener(new DocumentListener() { //XXX: listener
                 int version; //XXX: proper versioning!
                 @Override
@@ -180,17 +225,58 @@ public class TextDocumentSyncServerCapabilityHandler {
                 @Override
                 public void changedUpdate(DocumentEvent e) {}
             });
-        }
+            return 0;
+        });
     }
 
-    private void ensureOpenedInServer(JTextComponent opened) {
-        FileObject file = 
NbEditorUtilities.getFileObject(opened.getDocument());
+    private synchronized void editorOpened(JTextComponent c) {
+        Document doc = c.getDocument();
+        FileObject file = NbEditorUtilities.getFileObject(c.getDocument());
 
         if (file == null)
             return; //ignore
 
-        Document doc = opened.getDocument();
+        documentOpened(doc);
+        openDocument2PanesCount.compute(doc, (d, count) -> count + 1);
+    }
+
+    private synchronized void editorClosed(JTextComponent c) {
+        Document doc = c.getDocument();
+        Integer count = openDocument2PanesCount.getOrDefault(doc, -1);
+        if (count > 0) {
+            openDocument2PanesCount.put(doc, --count);
+        }
+        if (count == 0) {
+            //TODO modified!
+            WORKER.post(() -> {
+                FileObject file = NbEditorUtilities.getFileObject(doc);
+
+                if (file == null)
+                    return; //ignore
+
+                LSPBindings server = LSPBindings.getBindings(file);
+
+                if (server == null)
+                    return ; //ignore
+
+                TextDocumentIdentifier di = new TextDocumentIdentifier();
+                di.setUri(Utils.toURI(file));
+                DidCloseTextDocumentParams params = new 
DidCloseTextDocumentParams(di);
+
+                server.getTextDocumentService().didClose(params);
+                server.getOpenedFiles().remove(file);
+            });
+            openDocument2PanesCount.remove(doc);
+        }
+    }
+
+    private void ensureDidOpenSent(Document doc) {
         WORKER.post(() -> {
+            FileObject file = NbEditorUtilities.getFileObject(doc);
+
+            if (file == null)
+                return; //ignore
+
             LSPBindings server = LSPBindings.getBindings(file);
 
             if (server == null)
@@ -201,8 +287,6 @@ public class TextDocumentSyncServerCapabilityHandler {
                 return ;
             }
 
-            doc.putProperty(HyperlinkProviderImpl.class, Boolean.TRUE);
-
             String uri = Utils.toURI(file);
             String[] text = new String[1];
 
@@ -221,38 +305,7 @@ public class TextDocumentSyncServerCapabilityHandler {
                                                                      text[0]);
 
             server.getTextDocumentService().didOpen(new 
DidOpenTextDocumentParams(textDocumentItem));
-            if (opened.getClientProperty(MarkOccurrences.class) == null) {
-                MarkOccurrences mo = new MarkOccurrences(opened);
-                LSPBindings.addBackgroundTask(file, mo);
-                opened.putClientProperty(MarkOccurrences.class, mo);
-            }
-            if (opened.getClientProperty(BreadcrumbsImpl.class) == null) {
-                BreadcrumbsImpl bi = new BreadcrumbsImpl(opened);
-                LSPBindings.addBackgroundTask(file, bi);
-                opened.putClientProperty(BreadcrumbsImpl.class, bi);
-            }
             server.scheduleBackgroundTasks(file);
         });
     }
-
-    public static void refreshOpenedFilesInServers() {
-        SwingUtilities.invokeLater(() -> {
-            assert SwingUtilities.isEventDispatchThread();
-            for (JTextComponent c : EditorRegistry.componentList()) {
-                h.ensureOpenedInServer(c);
-            }
-        });
-    }
-
-    private static final TextDocumentSyncServerCapabilityHandler h = new 
TextDocumentSyncServerCapabilityHandler();
-    @OnStart
-    public static class Init implements Runnable {
-
-        @Override
-        public void run() {
-            EditorRegistry.addPropertyChangeListener(evt -> h.handleChange());
-            SwingUtilities.invokeLater(() -> h.handleChange());
-        }
-        
-    }
 }
diff --git 
a/ide/lsp.client/test/unit/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandlerTest.java
 
b/ide/lsp.client/test/unit/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandlerTest.java
new file mode 100644
index 0000000..d7fe5a7
--- /dev/null
+++ 
b/ide/lsp.client/test/unit/src/org/netbeans/modules/lsp/client/bindings/TextDocumentSyncServerCapabilityHandlerTest.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.lsp.client.bindings;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.swing.JEditorPane;
+import javax.swing.SwingUtilities;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.StyledDocument;
+import org.eclipse.lsp4j.DidChangeConfigurationParams;
+import org.eclipse.lsp4j.DidChangeTextDocumentParams;
+import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
+import org.eclipse.lsp4j.DidCloseTextDocumentParams;
+import org.eclipse.lsp4j.DidOpenTextDocumentParams;
+import org.eclipse.lsp4j.DidSaveTextDocumentParams;
+import org.eclipse.lsp4j.InitializeParams;
+import org.eclipse.lsp4j.InitializeResult;
+import org.eclipse.lsp4j.Range;
+import org.eclipse.lsp4j.ServerCapabilities;
+import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
+import org.eclipse.lsp4j.TextDocumentIdentifier;
+import org.eclipse.lsp4j.TextDocumentItem;
+import org.eclipse.lsp4j.TextDocumentSyncKind;
+import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
+import org.eclipse.lsp4j.launch.LSPLauncher;
+import org.eclipse.lsp4j.services.LanguageServer;
+import org.eclipse.lsp4j.services.TextDocumentService;
+import org.eclipse.lsp4j.services.WorkspaceService;
+import org.junit.Test;
+import org.netbeans.api.editor.mimelookup.MimePath;
+import org.netbeans.junit.MockServices;
+import org.netbeans.modules.editor.NbEditorKit;
+import org.netbeans.modules.editor.lib2.EditorApiPackageAccessor;
+import org.netbeans.modules.lsp.client.Utils;
+import org.netbeans.modules.lsp.client.spi.LanguageServerProvider;
+import org.netbeans.spi.editor.mimelookup.MimeDataProvider;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.MIMEResolver;
+import org.openide.loaders.DataObject;
+import org.openide.text.CloneableEditorSupport;
+import org.openide.text.NbDocument;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author lahvac
+ */
+public class TextDocumentSyncServerCapabilityHandlerTest {
+
+    private static final String MIME_TYPE = "application/mock-txt";
+    private static final List<String> eventLog = new ArrayList<>();
+
+    @Test
+    public void testOpenClose() throws Exception {
+        MockServices.setServices(MimeDataProviderImpl.class, 
MockMimeResolver.class);
+
+        new TextDocumentSyncServerCapabilityHandler.Init().run();
+
+        FileObject folder = 
FileUtil.createMemoryFileSystem().getRoot().createFolder("myfolder");
+        FileObject file = folder.createData("data.mock-txt");
+        EditorCookie ec = file.getLookup().lookup(EditorCookie.class);
+        ((CloneableEditorSupport) ec).setMIMEType(MIME_TYPE);
+        Document doc = ec.openDocument();
+        JEditorPane pane = new JEditorPane() {
+            @Override
+            public boolean isFocusOwner() {
+                return true;
+            }
+        };
+
+        pane.setDocument(doc);
+
+        String uri = Utils.toURI(file);
+
+        SwingUtilities.invokeLater(() -> {
+            EditorApiPackageAccessor.get().register(pane);
+        });
+
+        assertEvents("didOpen: " + uri + "/" + MIME_TYPE + "/0/");
+
+        NbDocument.runAtomic((StyledDocument) doc, () -> {
+            try {
+                doc.insertString(0, "text", null);
+            } catch (BadLocationException ex) {
+                throw new IllegalStateException(ex);
+            }
+        });
+
+        assertEvents("didChange: " + uri + "/1/[0:0-0:0 => text]");
+
+        NbDocument.runAtomic((StyledDocument) doc, () -> {
+            try {
+                doc.remove(2, 1);
+            } catch (BadLocationException ex) {
+                throw new IllegalStateException(ex);
+            }
+        });
+
+        assertEvents("didChange: " + uri + "/2/[0:2-0:3 => ]");
+
+        
assertTrue(DataObject.getRegistry().getModifiedSet().contains(DataObject.find(file)));
+
+        //TODO: send save event:
+//        LifecycleManager.getDefault().saveAll();
+//
+//        assertEvents("didSave: " + uri);
+
+        SwingUtilities.invokeLater(() -> {
+            EditorApiPackageAccessor.get().forceRelease(pane);
+        });
+
+        assertEvents("didClose: " + uri);
+
+        SwingUtilities.invokeLater(() -> {
+            EditorApiPackageAccessor.get().register(pane);
+        });
+
+        assertEvents("didOpen: " + uri + "/" + MIME_TYPE + "/0/tet");
+
+        SwingUtilities.invokeLater(() -> {
+            EditorApiPackageAccessor.get().forceRelease(pane);
+        });
+
+        assertEvents("didClose: " + uri);
+    }
+
+    private void assertEvents(String... events) {
+        synchronized (eventLog) {
+            long timeout = System.currentTimeMillis() + 10000000;
+
+            while (System.currentTimeMillis() < timeout && eventLog.size() < 
events.length) {
+                try {
+                    eventLog.wait(timeout - System.currentTimeMillis());
+                } catch (InterruptedException ex) {
+                }
+            }
+            assertEquals(Arrays.asList(events), eventLog);
+            eventLog.clear();
+        }
+    }
+
+    public static final class MimeDataProviderImpl implements MimeDataProvider 
{
+        @Override
+        public Lookup getLookup(MimePath mp) {
+            assertEquals("application/mock-txt", mp.getPath());
+            return Lookups.fixed(new MockLSP(), new NbEditorKit() {
+                @Override
+                public String getContentType() {
+                    return "application/mock-txt";
+                }
+            });
+        }
+    }
+
+    public static final class MockLSP implements LanguageServerProvider {
+        @Override
+        public LanguageServerProvider.LanguageServerDescription 
startServer(Lookup lookup) {
+            try {
+                final MockProcess process = new MockProcess();
+                ServerSocket srv = new ServerSocket(0, 1, 
InetAddress.getLoopbackAddress());
+                Thread serverThread = new Thread(() -> {
+                    try {
+                        Socket server = srv.accept();
+
+                        LSPLauncher.createServerLauncher(new 
TestLanguageServer(), server.getInputStream(), 
server.getOutputStream()).startListening().get();
+                    } catch (Exception ex) {
+                        throw new IllegalStateException(ex);
+                    }
+                });
+                serverThread.start();
+                Socket client = new Socket(srv.getInetAddress(), 
srv.getLocalPort());
+
+                return 
LanguageServerProvider.LanguageServerDescription.create(client.getInputStream(),
 client.getOutputStream(), process);
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+
+    public final static class MockMimeResolver extends MIMEResolver {
+
+        public MockMimeResolver() {
+        }
+
+        @Override
+        public String findMIMEType(FileObject fo) {
+            return fo.hasExt("mock-txt") ? "application/mock-txt" : null;
+        }
+    }
+
+    static final class MockProcess extends Process {
+        final ByteArrayInputStream in;
+        final ByteArrayOutputStream out;
+
+        public MockProcess() {
+            this.in = new ByteArrayInputStream(new byte[0]);
+            this.out = new ByteArrayOutputStream();
+        }
+
+        @Override
+        public OutputStream getOutputStream() {
+            return out;
+        }
+
+        @Override
+        public InputStream getInputStream() {
+            return in;
+        }
+
+        @Override
+        public InputStream getErrorStream() {
+            return in;
+        }
+
+        @Override
+        public int waitFor() throws InterruptedException {
+            throw new InterruptedException();
+        }
+
+        @Override
+        public boolean isAlive() {
+            return true;
+        }
+
+        @Override
+        public int exitValue() {
+            return 0;
+        }
+
+        @Override
+        public void destroy() {
+        }
+    }
+
+    private static final class TestLanguageServer implements LanguageServer {
+
+        @Override
+        public CompletableFuture<InitializeResult> initialize(InitializeParams 
params) {
+            ServerCapabilities caps = new ServerCapabilities();
+            caps.setTextDocumentSync(TextDocumentSyncKind.Incremental);
+            InitializeResult initResult = new InitializeResult(caps);
+            return CompletableFuture.completedFuture(initResult);
+        }
+
+        @Override
+        public CompletableFuture<Object> shutdown() {
+            return CompletableFuture.completedFuture(null);
+        }
+
+        @Override
+        public void exit() {
+        }
+
+        @Override
+        public TextDocumentService getTextDocumentService() {
+            return new TextDocumentService() {
+                @Override
+                public void didOpen(DidOpenTextDocumentParams params) {
+                    TextDocumentItem td = params.getTextDocument();
+                    synchronized (eventLog) {
+                        eventLog.add("didOpen: " + td.getUri() + "/" + 
td.getLanguageId() + "/" + td.getVersion() + "/" + td.getText());
+                        eventLog.notifyAll();
+                    }
+                }
+
+                @Override
+                public void didChange(DidChangeTextDocumentParams params) {
+                    VersionedTextDocumentIdentifier td = 
params.getTextDocument();
+                    List<TextDocumentContentChangeEvent> changes = 
params.getContentChanges();
+                    synchronized (eventLog) {
+                        eventLog.add("didChange: " + td.getUri() + "/" + 
td.getVersion() + "/" + changes.stream().map(c -> range2String(c.getRange()) + 
" => " + c.getText()).collect(Collectors.joining(", ", "[", "]")));
+                        eventLog.notifyAll();
+                    }
+                }
+
+                @Override
+                public void didClose(DidCloseTextDocumentParams params) {
+                    TextDocumentIdentifier td = params.getTextDocument();
+                    synchronized (eventLog) {
+                        eventLog.add("didClose: " + td.getUri());
+                        eventLog.notifyAll();
+                    }
+                }
+
+                @Override
+                public void didSave(DidSaveTextDocumentParams params) {
+                    TextDocumentIdentifier td = params.getTextDocument();
+                    synchronized (eventLog) {
+                        eventLog.add("didSave: " + td.getUri() + "/" + 
params.getText());
+                        eventLog.notifyAll();
+                    }
+                }
+            };
+        }
+
+        @Override
+        public WorkspaceService getWorkspaceService() {
+            return new WorkspaceService() {
+                @Override
+                public void 
didChangeConfiguration(DidChangeConfigurationParams params) {
+                    throw new IllegalStateException("Should not be called.");
+                }
+
+                @Override
+                public void didChangeWatchedFiles(DidChangeWatchedFilesParams 
params) {
+                    throw new IllegalStateException("Should not be called.");
+                }
+            };
+        }
+
+    }
+
+    private static String range2String(Range range) {
+        return       range.getStart().getLine() + ":" + 
range.getStart().getCharacter() +
+               "-" + range.getEnd().getLine() + ":" + 
range.getEnd().getCharacter();
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to