diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleExecutor.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleExecutor.kt
index a400cdbdce2..63742ac672f 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleExecutor.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleExecutor.kt
@@ -36,7 +36,7 @@ public class KotlinConsoleExecutor(
if (inputText.isNotEmpty()) {
historyHighlighter.printNewCommandInHistory(inputText)
- submitCommand("$inputText\n")
+ submitCommand(inputText)
}
}
@@ -50,7 +50,7 @@ public class KotlinConsoleExecutor(
val xmlRes = "$XML_PREAMBLE" +
"" +
"${StringUtil.escapeXml(
- StringUtil.replace("${command.trim()}\n", SOURCE_CHARS, XML_REPLACEMENTS)
+ StringUtil.replace("$command\n", SOURCE_CHARS, XML_REPLACEMENTS)
)}" +
""
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleHistoryManager.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleHistoryManager.kt
index 01ae100f2f7..6431560dd61 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleHistoryManager.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleHistoryManager.kt
@@ -80,7 +80,7 @@ public class KotlinConsoleHistoryManager(private val runner: KotlinConsoleRunner
historyPos = Math.max(historyPos - 1, 0)
WriteCommandAction.runWriteCommandAction(project) {
- document.setText(history[historyPos].trim())
+ document.setText(history[historyPos])
EditorUtil.scrollToTheEnd(consoleEditor)
prevCaretOffset = 0
caret.moveToOffset(0)
@@ -96,8 +96,7 @@ public class KotlinConsoleHistoryManager(private val runner: KotlinConsoleRunner
historyPos = Math.min(historyPos + 1, history.size())
WriteCommandAction.runWriteCommandAction(project) {
- document.setText(if (historyPos == history.size()) unfinishedCommand else history[historyPos].trim())
- caret.moveToOffset(document.textLength)
+ document.setText(if (historyPos == history.size()) unfinishedCommand else history[historyPos])
prevCaretOffset = document.textLength
EditorUtil.scrollToTheEnd(consoleEditor)
}
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleKeeper.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleKeeper.kt
index d63aa06b912..aba6cfde4b8 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleKeeper.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleKeeper.kt
@@ -46,7 +46,7 @@ public class KotlinConsoleKeeper(val project: Project) {
fun run(module: Module, testMode: Boolean = false, previousCompilationFailed: Boolean = false): KotlinConsoleRunner? {
val path = module.moduleFilePath
- val cmdLine = createCommandLine(module) ?: return errorNotification(project, "Module SDK not found") let { null }
+ val cmdLine = createCommandLine(module) ?: return run { errorNotification(project, "Module SDK not found"); null }
val consoleRunner = KotlinConsoleRunner(module, cmdLine, testMode, previousCompilationFailed, project, REPL_TITLE, path)
consoleRunner.initAndRun()
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleRunner.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleRunner.kt
index fe2dfafc06b..5e712c35a24 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleRunner.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinConsoleRunner.kt
@@ -29,9 +29,10 @@ import com.intellij.openapi.editor.markup.*
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
+import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.console.actions.BuildAndRestartConsoleAction
import org.jetbrains.kotlin.console.actions.KtExecuteCommandAction
-import org.jetbrains.kotlin.console.gutter.IconPack
+import org.jetbrains.kotlin.console.gutter.IconWithTooltip
import org.jetbrains.kotlin.console.gutter.KotlinConsoleGutterContentProvider
import org.jetbrains.kotlin.console.gutter.KotlinConsoleIndicatorRenderer
import org.jetbrains.kotlin.console.gutter.ReplIcons
@@ -41,7 +42,6 @@ import org.jetbrains.kotlin.console.highlight.ReplColors
import org.jetbrains.kotlin.idea.JetLanguage
import java.awt.Color
import java.awt.Font
-import java.util.concurrent.ConcurrentHashMap
import kotlin.properties.Delegates
public class KotlinConsoleRunner(
@@ -53,10 +53,11 @@ public class KotlinConsoleRunner(
title: String,
path: String?
) : AbstractConsoleRunnerWithHistory(myProject, title, path) {
- private val editorToHighlighter = ConcurrentHashMap()
private val historyManager = KotlinConsoleHistoryManager(this)
private val historyHighlighter = KotlinHistoryHighlighter(this)
- private var disposableDescriptor: RunContentDescriptor by Delegates.notNull()
+
+ private var consoleEditorHighlighter by Delegates.notNull()
+ private var disposableDescriptor by Delegates.notNull()
val executor = KotlinConsoleExecutor(this, historyManager, historyHighlighter)
var compilerHelper: KotlinConsoleCompilerHelper by Delegates.notNull()
@@ -137,7 +138,7 @@ public class KotlinConsoleRunner(
}
fun setupGutters() {
- fun configureEditorGutter(editor: EditorEx, color: Color, iconPack: IconPack) {
+ fun configureEditorGutter(editor: EditorEx, color: Color, iconWithTooltip: IconWithTooltip): RangeHighlighter {
editor.settings.isLineMarkerAreaShown = true // hack to show gutter
editor.settings.isFoldingOutlineShown = true
editor.gutterComponentEx.setPaintBackground(true)
@@ -145,41 +146,37 @@ public class KotlinConsoleRunner(
editorColorScheme.setColor(EditorColors.GUTTER_BACKGROUND, color)
editor.colorsScheme = editorColorScheme
- addGutterIndicator(editor, iconPack)
+ return addGutterIndicator(editor, iconWithTooltip)
}
val historyEditor = consoleView.historyViewer
val consoleEditor = consoleView.consoleEditor
configureEditorGutter(historyEditor, ReplColors.HISTORY_GUTTER_COLOR, ReplIcons.HISTORY_INDICATOR)
- configureEditorGutter(consoleEditor, ReplColors.EDITOR_GUTTER_COLOR, ReplIcons.EDITOR_INDICATOR)
+ consoleEditorHighlighter = configureEditorGutter(consoleEditor, ReplColors.EDITOR_GUTTER_COLOR, ReplIcons.EDITOR_INDICATOR)
historyEditor.settings.isUseSoftWraps = true
historyEditor.settings.additionalLinesCount = 0
- historyEditor.scrollPane.horizontalScrollBar.isEnabled = true
consoleEditor.settings.isCaretRowShown = true
consoleEditor.settings.additionalLinesCount = 2
}
- fun addGutterIndicator(editor: EditorEx, iconPack: IconPack) {
- val indicator = KotlinConsoleIndicatorRenderer(iconPack)
+ fun addGutterIndicator(editor: EditorEx, iconWithTooltip: IconWithTooltip): RangeHighlighter {
+ val indicator = KotlinConsoleIndicatorRenderer(iconWithTooltip)
val editorMarkup = editor.markupModel
val indicatorHighlighter = editorMarkup.addRangeHighlighter(
0, editor.document.textLength, HighlighterLayer.LAST, null, HighlighterTargetArea.LINES_IN_RANGE
)
- editorToHighlighter[editor] = indicatorHighlighter
- indicatorHighlighter.gutterIconRenderer = indicator
+ return indicatorHighlighter apply { gutterIconRenderer = indicator }
}
- fun changeConsoleEditorIndicator(newIconPack: IconPack) {
- val editorHighlighter = editorToHighlighter[consoleView.consoleEditor]
- editorHighlighter?.gutterIconRenderer = KotlinConsoleIndicatorRenderer(newIconPack)
+ fun changeConsoleEditorIndicator(newIconWithTooltip: IconWithTooltip) {
+ consoleEditorHighlighter.gutterIconRenderer = KotlinConsoleIndicatorRenderer(newIconWithTooltip)
}
- // this method shouldn't be called in normal usage; it is for test purpose only
- fun dispose() {
+ @TestOnly fun dispose() {
processHandler.destroyProcess()
Disposer.dispose(disposableDescriptor)
}
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinReplOutputHandler.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinReplOutputHandler.kt
index a90b3e6404a..ca75aaf97b1 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinReplOutputHandler.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/KotlinReplOutputHandler.kt
@@ -67,8 +67,8 @@ public class KotlinReplOutputHandler(
"READLINE_END" -> historyHighlighter.isReadLineMode = false
"REPL_INCOMPLETE",
"COMPILE_ERROR" -> outputHighlighter.highlightCompilerErrors(createCompilerMessages(content))
- "RUNTIME_ERROR" -> outputHighlighter.printRuntimeError(content)
- "INNER_ERROR" -> logError(javaClass, content)
+ "RUNTIME_ERROR" -> outputHighlighter.printRuntimeError("${content.trim()}\n")
+ "INNER_ERROR" -> logError(this.javaClass, content)
}
}
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/BuildAndRestartConsoleAction.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/BuildAndRestartConsoleAction.kt
deleted file mode 100644
index 70619a21513..00000000000
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/BuildAndRestartConsoleAction.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2010-2015 JetBrains s.r.o.
- *
- * Licensed 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.jetbrains.kotlin.console.actions
-
-import com.intellij.icons.AllIcons
-import com.intellij.openapi.actionSystem.AnAction
-import com.intellij.openapi.actionSystem.AnActionEvent
-import org.jetbrains.kotlin.console.KotlinConsoleRunner
-
-public class BuildAndRestartConsoleAction(
- private val runner: KotlinConsoleRunner
-) : AnAction("Build and restart", "Build module '${runner.module.name}' and restart", AllIcons.Actions.Restart) {
-
- override fun actionPerformed(_: AnActionEvent) = runner.compilerHelper.compileModule()
-}
\ No newline at end of file
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/KotlinConsoleModuleDialog.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/KotlinConsoleModuleDialog.kt
index e4c174d6dbc..8beeee728f0 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/KotlinConsoleModuleDialog.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/KotlinConsoleModuleDialog.kt
@@ -36,7 +36,7 @@ public class KotlinConsoleModuleDialog(private val project: Project) {
if (modules.isEmpty()) return errorNotification(project, "No modules were found")
if (modules.size() == 1) return runConsole(modules.first())
- val moduleActions = modules sortBy { it.name } map { runAction(it) }
+ val moduleActions = modules sortedBy { it.name } map { createRunAction(it) }
val moduleGroup = DefaultActionGroup(moduleActions)
val modulePopup = JBPopupFactory.getInstance().createActionGroupPopup(
@@ -47,19 +47,19 @@ public class KotlinConsoleModuleDialog(private val project: Project) {
}
private fun getModule(dataContext: DataContext): Module? {
- val file = CommonDataKeys.VIRTUAL_FILE.getData(dataContext)
+ val file = CommonDataKeys.VIRTUAL_FILE.getData(dataContext) ?: return null
- if (file != null) {
- val moduleForFile = ModuleUtilCore.findModuleForFile(file, project)
- if (moduleForFile != null) return moduleForFile
- }
+ val moduleForFile = ModuleUtilCore.findModuleForFile(file, project)
+ if (moduleForFile != null) return moduleForFile
return null
}
- private fun runConsole(module: Module) = KotlinConsoleKeeper.getInstance(project).run(module) let { Unit }
+ private fun runConsole(module: Module) {
+ KotlinConsoleKeeper.getInstance(project).run(module)
+ }
- private fun runAction(module: Module) = object : AnAction(module.name) {
+ private fun createRunAction(module: Module) = object : AnAction(module.name) {
override fun actionPerformed(_: AnActionEvent) = runConsole(module)
}
}
\ No newline at end of file
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/RunExecuteActions.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/RunExecuteActions.kt
index 63d0ac4fff9..cffef1e808c 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/RunExecuteActions.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/RunExecuteActions.kt
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.console.actions
+import com.intellij.icons.AllIcons
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.notification.Notifications
@@ -25,6 +26,7 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
+import org.jetbrains.kotlin.console.KotlinConsoleRunner
fun errorNotification(project: Project?, message: String) {
val errorTag = "KOTLIN REPL ERROR"
@@ -50,4 +52,11 @@ public class KtExecuteCommandAction(private val consoleFile: VirtualFile) : AnAc
ktConsole.executor.executeCommand()
}
+}
+
+public class BuildAndRestartConsoleAction(
+ private val runner: KotlinConsoleRunner
+) : AnAction("Build and restart", "Build module '${runner.module.name}' and restart", AllIcons.Actions.Restart) {
+
+ override fun actionPerformed(_: AnActionEvent) = runner.compilerHelper.compileModule()
}
\ No newline at end of file
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/gutter/KotlinConsoleIndicatorRenderer.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/gutter/KotlinConsoleIndicatorRenderer.kt
index 37b8c4bcc65..9afb08c243b 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/gutter/KotlinConsoleIndicatorRenderer.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/gutter/KotlinConsoleIndicatorRenderer.kt
@@ -18,9 +18,9 @@ package org.jetbrains.kotlin.console.gutter
import com.intellij.openapi.editor.markup.GutterIconRenderer
-public class KotlinConsoleIndicatorRenderer(iconPack: IconPack) : GutterIconRenderer() {
- private val icon = iconPack.icon
- private val tooltip = iconPack.tooltip
+public class KotlinConsoleIndicatorRenderer(iconWithTooltip: IconWithTooltip) : GutterIconRenderer() {
+ private val icon = iconWithTooltip.icon
+ private val tooltip = iconWithTooltip.tooltip
override fun getIcon() = icon
override fun getTooltipText() = tooltip
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/gutter/ReplIcons.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/gutter/ReplIcons.kt
index 44e72da9ecc..0553ac38389 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/gutter/ReplIcons.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/gutter/ReplIcons.kt
@@ -20,19 +20,19 @@ import com.intellij.icons.AllIcons
import org.jetbrains.kotlin.idea.JetIcons
import javax.swing.Icon
-public data class IconPack(val icon: Icon, val tooltip: String)
+public data class IconWithTooltip(val icon: Icon, val tooltip: String?)
public object ReplIcons {
- public val BUILD_WARNING_INDICATOR: IconPack = IconPack(AllIcons.Ide.Warning_notifications, "Some issues with module ")
- public val HISTORY_INDICATOR: IconPack = IconPack(AllIcons.General.MessageHistory, "History of executed commands")
- public val EDITOR_INDICATOR: IconPack = IconPack(JetIcons.LAUNCH, "Write your commands here")
- public val EDITOR_READLINE_INDICATOR: IconPack = IconPack(AllIcons.General.Balloon, "Waiting for input...")
- public val COMMAND_MARKER: IconPack = IconPack(AllIcons.General.Run, "Executed command")
- public val READLINE_MARKER: IconPack = IconPack(AllIcons.Icons.Ide.SpeedSearchPrompt, "Input line")
+ public val BUILD_WARNING_INDICATOR: IconWithTooltip = IconWithTooltip(AllIcons.Ide.Warning_notifications, null)
+ public val HISTORY_INDICATOR: IconWithTooltip = IconWithTooltip(AllIcons.General.MessageHistory, "History of executed commands")
+ public val EDITOR_INDICATOR: IconWithTooltip = IconWithTooltip(JetIcons.LAUNCH, "Write your commands here")
+ public val EDITOR_READLINE_INDICATOR: IconWithTooltip = IconWithTooltip(AllIcons.General.Balloon, "Waiting for input...")
+ public val COMMAND_MARKER: IconWithTooltip = IconWithTooltip(AllIcons.General.Run, "Executed command")
+ public val READLINE_MARKER: IconWithTooltip = IconWithTooltip(AllIcons.Icons.Ide.SpeedSearchPrompt, "Input line")
// command result icons
- public val SYSTEM_HELP: IconPack = IconPack(AllIcons.Actions.Menu_help, "System help")
- public val RESULT: IconPack = IconPack(AllIcons.Vcs.Equal, "Result")
+ public val SYSTEM_HELP: IconWithTooltip = IconWithTooltip(AllIcons.Actions.Menu_help, "System help")
+ public val RESULT: IconWithTooltip = IconWithTooltip(AllIcons.Vcs.Equal, "Result")
public val COMPILER_ERROR: Icon = AllIcons.General.Error
- public val RUNTIME_EXCEPTION: IconPack = IconPack(AllIcons.General.BalloonWarning, "Runtime exception")
+ public val RUNTIME_EXCEPTION: IconWithTooltip = IconWithTooltip(AllIcons.General.BalloonWarning, "Runtime exception")
}
\ No newline at end of file
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/highlight/KotlinHistoryHighlighter.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/highlight/KotlinHistoryHighlighter.kt
index da69c3f2a13..045c5403c12 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/highlight/KotlinHistoryHighlighter.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/highlight/KotlinHistoryHighlighter.kt
@@ -60,9 +60,9 @@ public class KotlinHistoryHighlighter(private val runner: KotlinConsoleRunner )
consoleDocument.setText("")
addFoldingRegion(historyEditor, startOffset, endOffset, trimmedCommandText)
- historyEditor.markupModel let {
- it.addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST, null, HighlighterTargetArea.EXACT_RANGE)
- } apply {
+ historyEditor.markupModel.addRangeHighlighter(
+ startOffset, endOffset, HighlighterLayer.LAST, null, HighlighterTargetArea.EXACT_RANGE
+ ) apply {
val historyMarker = if (isReadLineMode) ReplIcons.READLINE_MARKER else ReplIcons.COMMAND_MARKER
gutterIconRenderer = KotlinConsoleIndicatorRenderer(historyMarker)
}
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/highlight/KotlinReplOutputHighlighter.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/highlight/KotlinReplOutputHighlighter.kt
index 8b3c1a92df0..adc77027a7f 100644
--- a/idea/idea-repl/src/org/jetbrains/kotlin/console/highlight/KotlinReplOutputHighlighter.kt
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/highlight/KotlinReplOutputHighlighter.kt
@@ -31,7 +31,7 @@ import com.intellij.psi.PsiDocumentManager
import org.jetbrains.kotlin.console.KotlinConsoleHistoryManager
import org.jetbrains.kotlin.console.KotlinConsoleRunner
import org.jetbrains.kotlin.console.SeverityDetails
-import org.jetbrains.kotlin.console.gutter.IconPack
+import org.jetbrains.kotlin.console.gutter.IconWithTooltip
import org.jetbrains.kotlin.console.gutter.KotlinConsoleErrorRenderer
import org.jetbrains.kotlin.console.gutter.KotlinConsoleIndicatorRenderer
import org.jetbrains.kotlin.console.gutter.ReplIcons
@@ -57,17 +57,17 @@ public class KotlinReplOutputHighlighter(
return Pair(oldLen, newLen)
}
- private fun printOutput(output: String, contentType: ConsoleViewContentType, iconPack: IconPack? = null) {
+ private fun printOutput(output: String, contentType: ConsoleViewContentType, iconWithTooltip: IconWithTooltip? = null) {
val (startOffset, endOffset) = textOffsets(output)
consoleView.print(output, contentType)
consoleView.flushDeferredText()
- if (iconPack == null) return
+ if (iconWithTooltip == null) return
historyMarkup.addRangeHighlighter(
startOffset, endOffset, HighlighterLayer.LAST, null, HighlighterTargetArea.EXACT_RANGE
- ) apply { gutterIconRenderer = KotlinConsoleIndicatorRenderer(iconPack) }
+ ) apply { gutterIconRenderer = KotlinConsoleIndicatorRenderer(iconWithTooltip) }
}
private fun printWarningMessage(message: String, isAddHyperlink: Boolean) = WriteCommandAction.runWriteCommandAction(project) {
@@ -110,9 +110,9 @@ public class KotlinReplOutputHighlighter(
fun highlightCompilerErrors(compilerMessages: List) = WriteCommandAction.runWriteCommandAction(project) {
val lastCommandStartOffset = historyDocument.textLength - historyManager.lastCommandLength
val lastCommandStartLine = historyDocument.getLineNumber(lastCommandStartOffset)
- val historyCommandRunIndicator = historyMarkup.allHighlighters filter {
+ val historyCommandRunIndicator = historyMarkup.allHighlighters.filter {
historyDocument.getLineNumber(it.startOffset) == lastCommandStartLine && it.gutterIconRenderer != null
- } first { true }
+ }.first()
val highlighterAndMessagesByLine = compilerMessages.filter {
it.severity == Severity.ERROR || it.severity == Severity.WARNING