diff --git a/ide/editor.lib/src/org/netbeans/editor/GlyphGutter.java b/ide/editor.lib/src/org/netbeans/editor/GlyphGutter.java index 87d5d5608cd4..626cd0a0df3f 100644 --- a/ide/editor.lib/src/org/netbeans/editor/GlyphGutter.java +++ b/ide/editor.lib/src/org/netbeans/editor/GlyphGutter.java @@ -531,6 +531,8 @@ public void run() { Shape pViewAlloc = pViewDesc.getAllocation(); Rectangle pViewRect = pViewAlloc.getBounds(); pViewRect.width = repaintWidth; + pViewRect.y += pViewDesc.getShadowHeight(); + pViewRect.height -= pViewDesc.getShadowHeight(); if (pViewRect.y >= endRepaintY) { if (LOG.isLoggable(Level.FINE)) { LOG.fine("GlyphGutter: pViewRect.y=" + pViewRect.y + " >= endRepaintY=" + // NOI18N diff --git a/ide/editor.lib2/nbproject/project.properties b/ide/editor.lib2/nbproject/project.properties index a539fb759979..c7277bfe7d63 100644 --- a/ide/editor.lib2/nbproject/project.properties +++ b/ide/editor.lib2/nbproject/project.properties @@ -16,7 +16,8 @@ # under the License. is.autoload=true -javac.source=1.8 +javac.source=17 +javac.target=17 javac.compilerargs=-Xlint:unchecked spec.version.base=2.50.0 diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java index 578e9bf0b88a..23a62b88b7d1 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/highlighting/HighlightsList.java @@ -160,13 +160,15 @@ public AttributeSet cutSameFont(Font defaultFont, int maxEndOffset, int wsEndOff // Extends beyond first highlight Font firstFont = ViewUtils.getFont(firstAttrs, defaultFont); Object firstPrependText = firstAttrs != null ? firstAttrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) : null; + Object firstShadowPrependText = firstAttrs != null ? firstAttrs.getAttribute(ViewUtils.KEY_SHADOW_TEXT_PREPEND) : null; int index = 1; while (true) { item = get(index); AttributeSet attrs = item.getAttributes(); Font font = ViewUtils.getFont(attrs, defaultFont); Object prependText = attrs != null ? attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) : null; - if (!font.equals(firstFont) || !Objects.equals(firstPrependText, prependText)) { // Stop at itemEndOffset + Object shadowPrependText = attrs != null ? attrs.getAttribute(ViewUtils.KEY_SHADOW_TEXT_PREPEND) : null; + if (!font.equals(firstFont) || !Objects.equals(firstPrependText, prependText) || !Objects.equals(firstShadowPrependText, shadowPrependText)) { // Stop at itemEndOffset if (index == 1) { // Just single attribute set cutStartItems(1); startOffset = itemEndOffset; // end offset of first item diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewChildren.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewChildren.java index 7bc3eb535220..3c9f461d1fcb 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewChildren.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewChildren.java @@ -350,7 +350,7 @@ int getNextVisualPositionY(DocumentView docView, int offset, Bias bias, Shape do pAlloc = getChildAllocation(docView, pIndex, docViewAlloc); } docView.op.getTextLayoutCache().activate(pView); - retOffset = pView.children.getNextVisualPositionY(pView, offset, bias, pAlloc, southDirection, biasRet, x); + retOffset = pView.getNextVisualPositionY(pView, offset, bias, pAlloc, southDirection, biasRet, x); if (retOffset == -1) { offset = -1; // Continue by entering the paragraph from outside } diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java index cfe34feb58e6..a1e32bf33a76 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/DocumentViewOp.java @@ -257,6 +257,7 @@ public final class DocumentViewOp private int defaultRowHeightInt; private int defaultAscentInt; + private float defaultDescent; private float defaultCharWidth; @@ -1087,6 +1088,7 @@ private void updateRowHeight(FontInfo fontInfo, boolean force) { ": defaultAscentInt from " + defaultAscentInt + " to " + fontInfo.ascentInt + "\n"); // NOI18N } defaultAscentInt = fontInfo.ascentInt; + defaultDescent = fontInfo.descent; } if (force || defaultRowHeightInt < fontInfo.rowHeightInt) { if (LOG.isLoggable(Level.FINE)) { @@ -1276,6 +1278,11 @@ public float getDefaultAscent() { return defaultAscentInt; } + public float getDefaultDescent() { + checkSettingsInfo(); + return defaultDescent; + } + /** * Return array of default: *
    diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java index 8d0c0b8631f2..55379ba94a85 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/HighlightsViewFactory.java @@ -254,7 +254,7 @@ public EditorView createView(int startOffset, int limitOffset, boolean forcedLim } private @NonNull EditorView wrapWithPrependedText(@NonNull EditorView origView, @NullAllowed AttributeSet attrs) { - if (attrs != null && attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) instanceof String) { + if (attrs != null && (attrs.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) instanceof String || attrs.getAttribute(ViewUtils.KEY_SHADOW_TEXT_PREPEND) instanceof String)) { return new PrependedTextView(documentView().op, attrs, origView); } diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphView.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphView.java index 1362f25277b7..fb37327c4bff 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphView.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphView.java @@ -21,11 +21,15 @@ import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Font; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; +import java.awt.font.TextLayout; import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Logger; import javax.swing.JComponent; import javax.swing.SwingConstants; @@ -113,6 +117,8 @@ public final class ParagraphView extends EditorView implements EditorView.Parent private int statusBits; // 44 + 4 = 48 bytes + private List shadowText; // 44 + 4 = 52 bytes + public ParagraphView(Position startPos) { super(null); setStartPosition(startPos); @@ -225,6 +231,39 @@ public void replace(int index, int removeLength, View[] addViews) { assert (removeLength == 0) : "Attempt to remove from null children length=" + removeLength; // NOI18N children = new ParagraphViewChildren(addViews.length); } + if (index == 0 && addViews != null) { + if (shadowText != null && !shadowText.isEmpty()) { + shadowText.clear(); + } + + AttributeSet attrs = addViews[0].getAttributes(); + Object shadowTextDataAttr = attrs != null ? attrs.getAttribute(ViewUtils.KEY_VERTICAL_SHADOW_TEXT_PREPEND) : null; + DocumentView docView = getDocumentView(); + DocumentViewOp op = docView.op; + float shadowTextHeight = 0; + float shadowTextWidth = 0; + + if (shadowTextDataAttr instanceof String shadowTextData) { + if (shadowText == null) { + shadowText = new ArrayList<>(); + } + Font shadowTextFont = ViewUtils.getFont(getAttributes(), op.getDefaultFont()); + for (String line : shadowTextData.split("\n")) { //XXX: performance! + //duplicated code: + if (line.isEmpty()) { + line = " "; + } + final TextLayout l = op.createTextLayout(line, shadowTextFont); + shadowText.add(l); + shadowTextHeight += height(l); //??? + shadowTextWidth = Math.max(shadowTextWidth, l.getAdvance()); //??? + } + } + + children.shadowHeight = shadowTextHeight; + children.shadowWidth = shadowTextWidth; + } + children.replace(this, index, removeLength, addViews); } @@ -356,7 +395,18 @@ public int getViewIndexChecked(double x, double y, Shape alloc) { @Override public Shape modelToViewChecked(int offset, Shape alloc, Bias bias) { checkChildrenNotNull(); - return children.modelToViewChecked(this, offset, alloc, bias); + Shape ret = children.modelToViewChecked(this, offset, alloc, bias); + if (shadowText != null) { + //XXX: cached shadowText height + Rectangle2D bounds = ret.getBounds2D(); + float shadowHeight = 0; + for (TextLayout l : shadowText) { + shadowHeight += height(l); + } + bounds.setRect(bounds.getX(), bounds.getY() + shadowHeight, bounds.getWidth(), bounds.getHeight()); + ret = bounds; + } + return ret; } @Override @@ -369,6 +419,24 @@ public int viewToModelChecked(double x, double y, Shape alloc, Bias[] biasReturn public void paint(Graphics2D g, Shape alloc, Rectangle clipBounds) { // The background is already cleared by BasicTextUI.paintBackground() which uses component.getBackground() checkChildrenNotNull(); + if (shadowText != null && !shadowText.isEmpty()) { + Rectangle2D.Double bounds = ViewUtils.shape2Bounds(alloc); + Rectangle2D.Double span = new Rectangle2D.Double(0, 0, 0, 0); + double y = bounds.y; + for (TextLayout l : shadowText) { + g.setColor(Color.gray); + float h = height(l); + span.setRect(bounds.x, y, l.getAdvance(), h); + HighlightsViewUtils.paintTextLayout(g, span, l, getDocumentView()); + y += h; + } + + bounds.height -= y - bounds.y; + bounds.y = y; + + alloc = bounds; + } + children.paint(this, g, alloc, clipBounds); if (getDocumentView().op.isGuideLinesEnable()) { @@ -507,6 +575,12 @@ public int getNextVisualPositionFromChecked(int offset, Bias bias, Shape alloc, retOffset = children.getNextVisualPositionY(this, offset, bias, alloc, direction == SwingConstants.SOUTH, biasRet, HighlightsViewUtils.getMagicX(docView, this, offset, bias, alloc)); + if (shadowText != null && direction == SwingConstants.SOUTH) { + //TODO: reuse computed height + for (TextLayout l : shadowText) { + retOffset += height(l); + } + } } else { retOffset = offset; } @@ -516,7 +590,19 @@ public int getNextVisualPositionFromChecked(int offset, Bias bias, Shape alloc, } return retOffset; } - + + //TODO: investigate: + int getNextVisualPositionY(ParagraphView pView, + int offset, Bias bias, Shape pAlloc, boolean southDirection, Bias[] biasRet, double x) { + int retOffset = children.getNextVisualPositionY(pView, offset, bias, pAlloc, southDirection, biasRet, x); +// if (shadowText != null && southDirection) { +// for (TextLayout l : shadowText) { +// retOffset += l.getAscent() + l.getDescent(); +// } +// } + return retOffset; + } + void releaseTextLayouts() { children = null; markChildrenInvalid(); @@ -629,4 +715,7 @@ protected String getDumpName() { return "PV"; } + private float height(TextLayout l) { + return (float) Math.ceil(l.getAscent() + l.getDescent()); + } } diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java index 3275a682472f..4bfb6133d9e2 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java @@ -75,17 +75,21 @@ public ParagraphViewChildren(int capacity) { boolean isWrapped() { return (wrapInfo != null); } - + + //TODO: move to ParagraphView: + float shadowHeight; + float shadowWidth; + /** * Height of * @return */ float height() { - return (wrapInfo == null) ? childrenHeight : wrapInfo.height(this); + return ((wrapInfo == null) ? childrenHeight : wrapInfo.height(this)) + shadowHeight; } float width() { - return (wrapInfo == null) ? (float) childrenWidth() : wrapInfo.width(); + return Math.max((wrapInfo == null) ? (float) childrenWidth() : wrapInfo.width(), shadowWidth); } double childrenWidth() { diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewDescriptor.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewDescriptor.java index 2e0b4b67de27..f9131153ed9a 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewDescriptor.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewDescriptor.java @@ -90,5 +90,9 @@ public float getAscent() { return docView.op.getDefaultAscent(); // Currently the ascent is global } + public float getShadowHeight() { + ParagraphViewChildren pViewChildren = docView.getParagraphView(pViewIndex).children; + return pViewChildren != null ? pViewChildren.shadowHeight : 0; + } } diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java index e1b8b4649ab0..888a50aa8bc7 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/PrependedTextView.java @@ -39,27 +39,50 @@ public final class PrependedTextView extends EditorView { private final EditorView delegate; private final TextLayout prependedTextLayout; private final double prependedTextWidth; + private final TextLayout shadowPrependedTextLayout; + private final double shadowPrependedTextWidth; public PrependedTextView(DocumentViewOp op, AttributeSet attributes, EditorView delegate) { super(null); this.attributes = attributes; this.delegate = delegate; - Font font = ViewUtils.getFont(attributes, op.getDefaultHintFont()); - prependedTextLayout = op.createTextLayout((String) attributes.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND), font); - // Advance represents the width of the full string, including leading - // and trailing spaces - float width = prependedTextLayout.getAdvance(); - // The prependTextWidth is rounded to full char widths, so that layout - // is not destroyed too much - double em = op.getDefaultCharWidth(); - prependedTextWidth = Math.ceil(width / em) * em; + + if (attributes.getAttribute(ViewUtils.KEY_SHADOW_TEXT_PREPEND) instanceof String shadowText) { + Font shadowTextFont = ViewUtils.getFont(attributes, op.getDefaultFont()); + shadowPrependedTextLayout = op.createTextLayout(shadowText, shadowTextFont); + // Advance represents the width of the full string, including leading + // and trailing spaces + float width = shadowPrependedTextLayout.getAdvance(); + // The prependTextWidth is rounded to full char widths, so that layout + // is not destroyed too much + double em = op.getDefaultCharWidth(); + shadowPrependedTextWidth = Math.ceil(width / em) * em; + } else { + shadowPrependedTextLayout = null; + shadowPrependedTextWidth = 0; + } + + if (attributes.getAttribute(ViewUtils.KEY_VIRTUAL_TEXT_PREPEND) instanceof String virtualText) { + Font font = ViewUtils.getFont(attributes, op.getDefaultHintFont()); + prependedTextLayout = op.createTextLayout(virtualText, font); + // Advance represents the width of the full string, including leading + // and trailing spaces + float width = prependedTextLayout.getAdvance(); + // The prependTextWidth is rounded to full char widths, so that layout + // is not destroyed too much + double em = op.getDefaultCharWidth(); + prependedTextWidth = Math.ceil(width / em) * em; + } else { + prependedTextLayout = null; + prependedTextWidth = 0; + } } @Override public float getPreferredSpan(int axis) { float superSpan = delegate.getPreferredSpan(axis); if (axis == View.X_AXIS) { - superSpan += prependedTextWidth; + superSpan += prependedTextWidth + shadowPrependedTextWidth; } return superSpan; } @@ -73,16 +96,16 @@ public AttributeSet getAttributes() { public Shape modelToViewChecked(int offset, Shape alloc, Bias bias) { Shape res = delegate.modelToViewChecked(offset, alloc, bias); Rectangle2D rect = ViewUtils.shapeAsRect(res); - rect.setRect(rect.getX() + prependedTextWidth, rect.getY(), rect.getWidth(), rect.getHeight()); + rect.setRect(rect.getX(), rect.getY(), rect.getWidth() + prependedTextWidth + shadowPrependedTextWidth, rect.getHeight()); return rect; } @Override public void paint(Graphics2D g, Shape hViewAlloc, Rectangle clipBounds) { Rectangle2D span = ViewUtils.shapeAsRect(hViewAlloc); - span.setRect(span.getX() + prependedTextWidth, span.getY(), span.getWidth() - prependedTextWidth, span.getHeight()); + span.setRect(span.getX() + prependedTextWidth + shadowPrependedTextWidth, span.getY(), span.getWidth() - prependedTextWidth - shadowPrependedTextWidth, span.getHeight()); delegate.paint(g, span, clipBounds); - span.setRect(span.getX() - prependedTextWidth, span.getY(), prependedTextWidth, span.getHeight()); + span.setRect(span.getX() - prependedTextWidth - shadowPrependedTextWidth, span.getY(), prependedTextWidth + shadowPrependedTextWidth, span.getHeight()); HighlightsSequence highlights = getDocumentView().getPaintHighlights(this, 0); @@ -92,9 +115,17 @@ public void paint(Graphics2D g, Shape hViewAlloc, Rectangle clipBounds) { HighlightsViewUtils.paintBackgroundHighlights(g, span, attrs, getDocumentView()); //TODO: clear some attributes (like boxes)??? } - g.setColor(Color.gray); - span.setRect(span.getX(), span.getY(), prependedTextWidth, span.getHeight()); - HighlightsViewUtils.paintTextLayout(g, span, prependedTextLayout, getDocumentView()); + if (shadowPrependedTextLayout != null) { + g.setColor(Color.gray); + span.setRect(span.getX(), span.getY(), shadowPrependedTextWidth, span.getHeight()); + HighlightsViewUtils.paintTextLayout(g, span, shadowPrependedTextLayout, getDocumentView()); + } + + if (prependedTextLayout != null) { + g.setColor(Color.gray); + span.setRect(span.getX() + shadowPrependedTextWidth, span.getY(), prependedTextWidth, span.getHeight()); + HighlightsViewUtils.paintTextLayout(g, span, prependedTextLayout, getDocumentView()); + } } ParagraphView getParagraphView() { @@ -119,8 +150,8 @@ public void setRawEndOffset(int offset) { @Override public int viewToModelChecked(double x, double y, Shape alloc, Position.Bias[] biasReturn) { Rectangle2D bounds = ViewUtils.shapeAsRect(alloc); - bounds.setRect(bounds.getX() + prependedTextWidth, bounds.getY(), - bounds.getWidth() - prependedTextWidth, bounds.getHeight()); + bounds.setRect(bounds.getX() + prependedTextWidth + shadowPrependedTextWidth, bounds.getY(), + bounds.getWidth() - prependedTextWidth + shadowPrependedTextWidth, bounds.getHeight()); if (x <= bounds.getX()) { return getStartOffset(); } diff --git a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java index a4c97adfd493..21098c782cec 100644 --- a/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java +++ b/ide/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java @@ -45,6 +45,8 @@ public final class ViewUtils { public static final String KEY_VIRTUAL_TEXT_PREPEND = "virtual-text-prepend"; //NOI18N + public static final String KEY_SHADOW_TEXT_PREPEND = "shadow-text-prepend"; //NOI18N + public static final String KEY_VERTICAL_SHADOW_TEXT_PREPEND = "vertical-shadow-text-prepend"; //NOI18N private ViewUtils() { // No instances } diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/EnhancedLanguageServer.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/EnhancedLanguageServer.java new file mode 100644 index 000000000000..7cfd418d5ee3 --- /dev/null +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/EnhancedLanguageServer.java @@ -0,0 +1,29 @@ +/* + * 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; + +import org.eclipse.lsp4j.jsonrpc.services.JsonDelegate; +import org.eclipse.lsp4j.services.LanguageServer; + +public interface EnhancedLanguageServer extends LanguageServer { + + @JsonDelegate + public EnhancedTextDocumentService getEnhancedTextDocumentService(); + +} diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/EnhancedTextDocumentService.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/EnhancedTextDocumentService.java new file mode 100644 index 000000000000..a2a3d860abea --- /dev/null +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/EnhancedTextDocumentService.java @@ -0,0 +1,114 @@ +/* + * 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; + +import java.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.services.TextDocumentService; + +public interface EnhancedTextDocumentService extends TextDocumentService { + @JsonRequest("textDocument/inlineCompletion") + public default CompletableFuture inlineCompletion(InlineCompletionParams params) { + throw new UnsupportedOperationException(); + } + + public static class InlineCompletionParams { + private TextDocumentIdentifier textDocument; + private Position position; + private InlineCompletionContext context; + + public InlineCompletionParams() { + } + + public InlineCompletionParams(TextDocumentIdentifier textDocument, Position position, InlineCompletionContext context) { + this.textDocument = textDocument; + this.position = position; + this.context = context; + } + + public TextDocumentIdentifier getTextDocument() { + return textDocument; + } + + public void setTextDocument(TextDocumentIdentifier textDocument) { + this.textDocument = textDocument; + } + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + + public InlineCompletionContext getContext() { + return context; + } + + public void setContext(InlineCompletionContext context) { + this.context = context; + } + + } + + public static class InlineCompletionContext { + private Object triggerKind; + private Object selectedCompletionInfo; + + public Object getTriggerKind() { + return triggerKind; + } + + public void setTriggerKind(Object triggerKind) { + this.triggerKind = triggerKind; + } + + public Object getSelectedCompletionInfo() { + return selectedCompletionInfo; + } + + public void setSelectedCompletionInfo(Object selectedCompletionInfo) { + this.selectedCompletionInfo = selectedCompletionInfo; + } + + } + + public static class InlineCompletionItem { + private String insertText; + + public InlineCompletionItem() { + } + + public InlineCompletionItem(String insertText) { + this.insertText = insertText; + } + + public String getInsertText() { + return insertText; + } + + public void setInsertText(String insertText) { + this.insertText = insertText; + } + + } +} diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java index 13a646a235c2..b9a1028594d7 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java @@ -313,15 +313,15 @@ private static void startFailed(ServerDescription description, String mimeType) try { LanguageClientImpl lci = new LanguageClientImpl(); - LanguageServer server = LanguageServerProviderAccessor.getINSTANCE().getServer(desc); + EnhancedLanguageServer server = LanguageServerProviderAccessor.getINSTANCE().getServer(desc); Process process; if (server == null) { InputStream in = LanguageServerProviderAccessor.getINSTANCE().getInputStream(desc); OutputStream out = LanguageServerProviderAccessor.getINSTANCE().getOutputStream(desc); process = LanguageServerProviderAccessor.getINSTANCE().getProcess(desc); - Launcher.Builder launcherBuilder = new LSPLauncher.Builder() + Launcher.Builder launcherBuilder = new LSPLauncher.Builder() .setLocalService(lci) - .setRemoteInterface(LanguageServer.class) + .setRemoteInterface(EnhancedLanguageServer.class) .setInput(in) .setOutput(out) .configureGson(gson -> { @@ -361,7 +361,7 @@ public void close() throws IOException { }); launcherBuilder.traceMessages(pw); } - Launcher launcher = launcherBuilder.create(); + Launcher launcher = launcherBuilder.create(); launcher.startListening(); server = launcher.getRemoteProxy(); } else { @@ -414,16 +414,23 @@ public static void addBindings(FileObject root, int port, String... extensions) LanguageClientImpl lc = new LanguageClientImpl(); InputStream in = s.getInputStream(); OutputStream out = s.getOutputStream(); - Launcher launcher = LSPLauncher.createClientLauncher(lc, in, new OutputStream() { + final OutputStream wrappedOut = new OutputStream() { @Override public void write(int w) throws IOException { out.write(w); if (w == '\n') out.flush(); } - }); + }; +// Launcher launcher = LSPLauncher.createClientLauncher(lc, in, wrappedOut); + Launcher launcher = new Launcher.Builder() + .setLocalService(lc) + .setRemoteInterface(EnhancedLanguageServer.class) + .setInput(in) + .setOutput(out) + .create(); launcher.startListening(); - LanguageServer server = launcher.getRemoteProxy(); + EnhancedLanguageServer server = launcher.getRemoteProxy(); InitializeResult result = initServer(null, server, root); server.initialized(new InitializedParams()); LSPBindings bindings = new LSPBindings(server, result, null); @@ -518,18 +525,18 @@ public static synchronized Set getAllBindings() { return allBindings; } - private final LanguageServer server; + private final EnhancedLanguageServer server; private final InitializeResult initResult; private final Process process; - private LSPBindings(LanguageServer server, InitializeResult initResult, Process process) { + private LSPBindings(EnhancedLanguageServer server, InitializeResult initResult, Process process) { this.server = server; this.initResult = initResult; this.process = process; } - public TextDocumentService getTextDocumentService() { - return server.getTextDocumentService(); + public EnhancedTextDocumentService getTextDocumentService() { + return server.getEnhancedTextDocumentService(); } public WorkspaceService getWorkspaceService() { diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LanguageServerProviderAccessor.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LanguageServerProviderAccessor.java index 9fab198d7a19..7961ab5990ec 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LanguageServerProviderAccessor.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LanguageServerProviderAccessor.java @@ -53,8 +53,8 @@ public static void setINSTANCE (LanguageServerProviderAccessor instance) { public abstract InputStream getInputStream(LanguageServerDescription desc); public abstract OutputStream getOutputStream(LanguageServerDescription desc); public abstract Process getProcess(LanguageServerDescription desc); - public abstract LanguageServer getServer(LanguageServerDescription desc); + public abstract EnhancedLanguageServer getServer(LanguageServerDescription desc); public abstract LSPBindings getBindings(LanguageServerDescription desc); public abstract void setBindings(LanguageServerDescription desc, LSPBindings bindings); - public abstract LanguageServerDescription createLanguageServerDescription(@NonNull LanguageServer server); + public abstract LanguageServerDescription createLanguageServerDescription(@NonNull EnhancedLanguageServer server); } diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java index 7bd4b2b8abda..89f87a393ce4 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/Utils.java @@ -31,6 +31,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; @@ -435,4 +436,45 @@ public interface Environment { public void handleCancellationException(CancellationException ex); public void handleException(Exception ex); } + + public static class CancelableBaseEnvironment implements Environment { + private final AtomicBoolean cancel = new AtomicBoolean(); + private final List cancelCallbacks = new ArrayList<>(); + + public void cancelRequest() { + cancel.set(true); + List localCancelCallbacks; + synchronized (cancelCallbacks) { + localCancelCallbacks = new ArrayList<>(cancelCallbacks); + } + localCancelCallbacks.forEach(Runnable::run); + } + + @Override + public boolean isCanceled() { + return cancel.get(); + } + + @Override + public void registerCancelCallback(Runnable callback) { + synchronized (cancelCallbacks) { + cancelCallbacks.add(callback); + } + + if (cancel.get()) { + callback.run(); + } + } + + @Override + public void handleCancellationException(CancellationException ex) { + //ignore cancels + } + + @Override + public void handleException(Exception ex) { + Exceptions.printStackTrace(ex); + } + + } } diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/InlineCompletion.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/InlineCompletion.java new file mode 100644 index 000000000000..52256325aa8d --- /dev/null +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/InlineCompletion.java @@ -0,0 +1,352 @@ +/* + * 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.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.text.Position; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.netbeans.api.editor.*; +import org.netbeans.api.editor.document.LineDocument; +import org.netbeans.api.editor.document.LineDocumentUtils; +import org.netbeans.api.editor.mimelookup.MimeRegistration; +import org.netbeans.api.editor.settings.AttributesUtilities; +import org.netbeans.editor.BaseKit.InsertTabAction; +import org.netbeans.lib.editor.util.swing.DocumentUtilities; +import org.netbeans.modules.editor.NbEditorUtilities; +import org.netbeans.modules.editor.indent.api.IndentUtils; +import org.netbeans.modules.lsp.client.EnhancedTextDocumentService.InlineCompletionParams; +import org.netbeans.modules.lsp.client.LSPBindings; +import org.netbeans.modules.lsp.client.Utils; +import org.netbeans.modules.lsp.client.Utils.CancelableBaseEnvironment; +import org.netbeans.spi.editor.AbstractEditorAction; +import org.netbeans.spi.editor.highlighting.HighlightsLayer; +import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; +import org.netbeans.spi.editor.highlighting.ZOrder; +import org.netbeans.spi.editor.highlighting.support.OffsetsBag; +import org.openide.filesystems.FileObject; +import org.openide.modules.OnStart; +import org.openide.util.Exceptions; +import org.openide.util.NbBundle.Messages; +import org.openide.util.RequestProcessor; +import org.openide.util.RequestProcessor.Task; + +//TODO: shutdown +public class InlineCompletion { + private static final RequestProcessor PREPARE_WORKER = new RequestProcessor(InlineCompletion.class.getName() + "-prepare", 1, false, false); + private static final RequestProcessor RUN_WORKER = new RequestProcessor(InlineCompletion.class.getName() + "-run", 1, false, false); + + @OnStart + public static class Start implements Runnable { + + @Override + public void run() { + EditorRegistry.addPropertyChangeListener(new EditorListener()); + } + + } + + private static class EditorListener implements PropertyChangeListener, CaretListener { + + public EditorListener() { + } + + private final Set openComponents = new HashSet<>(); + private JTextComponent lastComponent; + private AtomicReference currentFile = new AtomicReference<>(); + private AtomicReference currentDocument = new AtomicReference<>(); + private AtomicInteger currentCaretPos = new AtomicInteger(); + private AtomicReference currentQuery = new AtomicReference<>(); + private final Task query = PREPARE_WORKER.create(this::doQuery); + + @Override + public void propertyChange(PropertyChangeEvent evt) { + JTextComponent c = EditorRegistry.lastFocusedComponent(); + if (lastComponent != c) { + if (lastComponent != null) { + lastComponent.removeCaretListener(this); + } + lastComponent = c; + if (c != null) { + FileObject file = NbEditorUtilities.getFileObject(c.getDocument()); + currentFile.set(file); + currentDocument.set(c.getDocument()); + currentCaretPos.set(-1); + boolean wasOpen = openComponents.contains(c); + openComponents.clear(); + openComponents.addAll(EditorRegistry.componentList()); + c.addCaretListener(this); + } else { + currentFile.set(null); + currentDocument.set(null); + } + } + } + + @Override + public void caretUpdate(CaretEvent e) { + currentCaretPos.set(e.getDot()); + scheduleQuery(); + } + + private void scheduleQuery() { + query.schedule(500); + } + + private void doQuery() { + //TODO: only if the rest of the line is empty?? + FileObject file = currentFile.get(); + Document doc = currentDocument.get(); + int caretPos = currentCaretPos.get(); + + if (file != null && doc != null && caretPos != (-1)) { + ProposalItem existingProposal = ProposalItem.get(doc); + long thisVersion = DocumentUtilities.getDocumentVersion(doc); + if (existingProposal != null) { + //TODO: tracking typing modifications: + if (existingProposal.documentVersion == thisVersion && + caretPos == existingProposal.location.getOffset()) { + //skip + return ; + } + ProposalItem.clearProposal(doc); + } + + CancelableBaseEnvironment currentRunningQuery = currentQuery.get(); + + if (currentRunningQuery != null) { + currentRunningQuery.cancelRequest(); + currentQuery.compareAndSet(currentRunningQuery, null); + } + + CancelableBaseEnvironment env = new CancelableBaseEnvironment(); + currentQuery.set(env); + + RUN_WORKER.post(() -> { + if (env.isCanceled()) { + return ; + } + + List bindings = LSPBindings.getBindings(file); + AtomicBoolean wasProposal = new AtomicBoolean(); + + Utils.handleBindings(bindings, + capa -> true, //TODO + () -> new InlineCompletionParams(new TextDocumentIdentifier(Utils.toURI(file)), Utils.createPosition(doc, caretPos), null), + (server, params) -> server.getTextDocumentService() + .inlineCompletion(params), + (server, proposals) -> { + if (wasProposal.getAndSet(true)) { + //only first: + return ; + } + if (proposals != null && proposals.length > 0 && thisVersion == DocumentUtilities.getDocumentVersion(doc)) { + //TODO: more proper re-indent possible? + try { + int indent = IndentUtils.lineIndent(doc, IndentUtils.lineStartOffset(doc, caretPos)); + String proposalText = proposals[0].getInsertText().replaceAll("\n", "\n" + IndentUtils.createIndentString(doc, indent)); + ProposalItem.putProposal(server, doc, caretPos, proposalText); + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + } + }, + env + ); + }); + } + } + } + + private static class ProposalItem implements DocumentListener { + private final LSPBindings bindings; + public final Position location; + public final long documentVersion; + public final String text; + + private ProposalItem(LSPBindings bindings, Position location, long documentVersion, String text) { + this.bindings = bindings; + this.location = location; + this.documentVersion = documentVersion; + this.text = text; + } + + public static void putProposal(LSPBindings bindings, Document doc, int pos, String text) { + doc.render(() -> { + try { + LineDocument ldoc = LineDocumentUtils.asRequired(doc, LineDocument.class); + //TODO: should take the version at the time the proposal was triggered? + ProposalItem i = new ProposalItem(bindings, ldoc.createPosition(pos, /*XXX*/Position.Bias.Forward), DocumentUtilities.getDocumentVersion(doc), text); + doc.putProperty(ProposalItem.class, i); + i.setShadowText(doc, pos); + doc.addDocumentListener(i); + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + }); + } + + public static void clearProposal(Document doc) { + ProposalItem i = (ProposalItem) doc.getProperty(ProposalItem.class); + doc.putProperty(ProposalItem.class, null); + if (i != null) { + i.clearShadowText(doc); + doc.removeDocumentListener(i); + } + } + + public static ProposalItem get(Document doc) { + return (ProposalItem) doc.getProperty(ProposalItem.class); + } + + private void setShadowText(Document doc, int caretPos) { + OffsetsBag bag = new OffsetsBag(doc); + int common = caretPos - location.getOffset(); + try { + if (common < 0 || !text.startsWith(doc.getText(location.getOffset(), common))) { + clearProposal(doc); + return ; + } + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + return ; + } + String reducedText = text.substring(common); + String[] firstLineAndTheRest = reducedText.split("\n", 2); + + bag.addHighlight(caretPos, caretPos + 1, AttributesUtilities.createImmutable("shadow-text-prepend", firstLineAndTheRest[0])); + + if (firstLineAndTheRest.length > 1) { + bag.addHighlight(caretPos + 1, caretPos + 2, AttributesUtilities.createImmutable("vertical-shadow-text-prepend", firstLineAndTheRest[1])); + } + + getShadowTextBag(doc).setHighlights(bag); + } + + private void clearShadowText(Document doc) { + OffsetsBag bag = new OffsetsBag(doc); + + getShadowTextBag(doc).setHighlights(bag); + } + + @Override + public void insertUpdate(DocumentEvent e) { + Document doc = e.getDocument(); + ProposalItem i = get(doc); + + if (i == null) { + return ; + } + + if (!DocumentUtilities.isTypingModification(doc)) { + clearProposal(doc); + } + + i.setShadowText(doc, e.getOffset()); + } + + @Override + public void removeUpdate(DocumentEvent e) { + Document doc = e.getDocument(); + ProposalItem i = get(doc); + + if (i == null) { + return ; + } + + if (!DocumentUtilities.isTypingModification(doc)) { + clearProposal(doc); + } + + i.setShadowText(doc, e.getOffset()); + } + + @Override + public void changedUpdate(DocumentEvent e) { + } + + } + + private static final Object KEY_SHADOW_TEXT = new Object(); + static OffsetsBag getShadowTextBag(Document doc) { + OffsetsBag bag = (OffsetsBag) doc.getProperty(KEY_SHADOW_TEXT); + + if (bag == null) { + doc.putProperty(KEY_SHADOW_TEXT, bag = new OffsetsBag(doc)); + } + + return bag; + } + + @MimeRegistration(mimeType="", service=HighlightsLayerFactory.class) + public static class HighlightsLayerFactoryImpl implements HighlightsLayerFactory { + + public HighlightsLayer[] createLayers(HighlightsLayerFactory.Context context) { + return new HighlightsLayer[] { + HighlightsLayer.create(InlineCompletion.class.getName(), ZOrder.SYNTAX_RACK.forPosition(1700), false, getShadowTextBag(context.getDocument())), + }; + } + + } + + @EditorActionRegistration(name = "lsp-inline-completion-proposal-accept") + @Messages("lsp-inline-completion-proposal-accept=Inline Completion Proposal Accept") + public static class ConfimAction extends AbstractEditorAction { + + private final InsertTabAction delegate = new InsertTabAction(); + + @Override + protected void actionPerformed(ActionEvent evt, final JTextComponent target) { + if (target != null) { + ProposalItem i = ProposalItem.get(target.getDocument()); + + if (i != null) { + int caret = target.getCaret().getDot(); + Document doc = target.getDocument(); + + try { + doc.insertString(caret, i.text, null); + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + + ProposalItem.clearProposal(doc); + } else { + //XXx: + delegate.actionPerformed(evt, target); + } + } + } + + } +} diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/refactoring/Refactoring.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/refactoring/Refactoring.java index 6c521df6e630..4f545ef0013a 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/refactoring/Refactoring.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/refactoring/Refactoring.java @@ -53,6 +53,7 @@ import org.netbeans.api.queries.FileEncodingQuery; import org.netbeans.modules.lsp.client.LSPBindings; import org.netbeans.modules.lsp.client.Utils; +import org.netbeans.modules.lsp.client.Utils.CancelableBaseEnvironment; import org.netbeans.modules.lsp.client.Utils.Environment; import org.netbeans.modules.lsp.client.bindings.refactoring.ModificationResult.Difference; import org.netbeans.modules.lsp.client.bindings.refactoring.tree.DiffElement; @@ -333,32 +334,9 @@ private static String uri2SimpleName(String uri) { return uri.substring(dot + 1); } - protected static class RefactoringBase implements Environment { - private final AtomicBoolean cancel = new AtomicBoolean(); - private final List cancelCallbacks = new ArrayList<>(); + protected static class RefactoringBase extends CancelableBaseEnvironment { private Problem problem; - public void cancelRequest() { - cancel.set(true); - List localCancelCallbacks; - synchronized (cancelCallbacks) { - localCancelCallbacks = new ArrayList<>(cancelCallbacks); - } - localCancelCallbacks.forEach(Runnable::run); - } - - @Override - public boolean isCanceled() { - return cancel.get(); - } - - @Override - public void registerCancelCallback(Runnable callback) { - synchronized (cancelCallbacks) { - cancelCallbacks.add(callback); - } - } - @Override public void handleCancellationException(CancellationException ex) { addProblem(new Problem(false, Bundle.TXT_Canceled())); diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bridge/BridgingLanguageServer.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bridge/BridgingLanguageServer.java index 700d723228b6..4b5d7591acff 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bridge/BridgingLanguageServer.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bridge/BridgingLanguageServer.java @@ -74,6 +74,8 @@ import org.netbeans.api.lsp.Completion.Context; import org.netbeans.api.lsp.Completion.TriggerKind; import org.netbeans.api.lsp.StructureElement; +import org.netbeans.modules.lsp.client.EnhancedLanguageServer; +import org.netbeans.modules.lsp.client.EnhancedTextDocumentService; import org.netbeans.modules.lsp.client.Utils; import org.netbeans.spi.lsp.ErrorProvider; import org.netbeans.spi.lsp.StructureProvider; @@ -82,7 +84,7 @@ import org.openide.util.RequestProcessor; import org.openide.util.RequestProcessor.Task; -public class BridgingLanguageServer implements LanguageServer, LanguageClientAware { +public class BridgingLanguageServer implements EnhancedLanguageServer, LanguageClientAware { private static final int DIAGNOSTIC_DELAY = 500; private static final RequestProcessor WORKER = new RequestProcessor(BridgingLanguageServer.class.getName() + "-worker", 1, false, false); @@ -147,8 +149,8 @@ private void reRunDiagnostics(FileObject file) { } @Override - public TextDocumentService getTextDocumentService() { - return new TextDocumentService() { + public EnhancedTextDocumentService getTextDocumentService() { + return new EnhancedTextDocumentService() { @Override public void didOpen(DidOpenTextDocumentParams params) { FileObject file = Utils.fromURI(params.getTextDocument().getUri()); @@ -262,6 +264,11 @@ public CompletableFuture>> docume }; } + @Override + public EnhancedTextDocumentService getEnhancedTextDocumentService() { + return getTextDocumentService(); + } + private CompletionItem convertCompletionItem(Document doc, Completion completion) { CompletionItem item = new CompletionItem(completion.getLabel()); if (completion.getLabelDetail() != null || completion.getLabelDescription() != null) { diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/layer.xml b/ide/lsp.client/src/org/netbeans/modules/lsp/client/layer.xml index 564f6b5c8478..d64f58826294 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/layer.xml +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/layer.xml @@ -22,6 +22,13 @@ + + + + + + + diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/NetBeans-keybindings.xml b/ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/NetBeans-keybindings.xml new file mode 100644 index 000000000000..7cbda61b8017 --- /dev/null +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/resources/NetBeans-keybindings.xml @@ -0,0 +1,32 @@ + + + + + + + + + + diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/spi/LanguageServerProvider.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/spi/LanguageServerProvider.java index eea786f3fe34..2b0eee84f906 100644 --- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/spi/LanguageServerProvider.java +++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/spi/LanguageServerProvider.java @@ -24,6 +24,7 @@ import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.modules.lsp.client.EnhancedLanguageServer; import org.netbeans.modules.lsp.client.LSPBindings; import org.netbeans.modules.lsp.client.LanguageServerProviderAccessor; import org.openide.util.Lookup; @@ -61,17 +62,17 @@ public static final class LanguageServerDescription { return new LanguageServerDescription(in, out, process, null); } - static @NonNull LanguageServerDescription create(@NonNull LanguageServer server) { + static @NonNull LanguageServerDescription create(@NonNull EnhancedLanguageServer server) { return new LanguageServerDescription(null, null, null, server); } private final InputStream in; private final OutputStream out; private final Process process; - private final LanguageServer server; + private final EnhancedLanguageServer server; private LSPBindings bindings; - private LanguageServerDescription(InputStream in, OutputStream out, Process process, LanguageServer server) { + private LanguageServerDescription(InputStream in, OutputStream out, Process process, EnhancedLanguageServer server) { this.in = in; this.out = out; this.process = process; @@ -96,7 +97,7 @@ public Process getProcess(LanguageServerDescription desc) { } @Override - public LanguageServer getServer(LanguageServerDescription desc) { + public EnhancedLanguageServer getServer(LanguageServerDescription desc) { return desc.server; } @@ -111,7 +112,7 @@ public void setBindings(LanguageServerDescription desc, LSPBindings bindings) { } @Override - public LanguageServerDescription createLanguageServerDescription(LanguageServer server) { + public LanguageServerDescription createLanguageServerDescription(EnhancedLanguageServer server) { return LanguageServerDescription.create(server); } });