Skip to content

Instantly share code, notes, and snippets.

@rakete
Last active January 7, 2025 04:31
Show Gist options
  • Save rakete/ca02db2000e1cf122c083773e4045190 to your computer and use it in GitHub Desktop.
Save rakete/ca02db2000e1cf122c083773e4045190 to your computer and use it in GitHub Desktop.
Rider Live Plugin for using Tab for both Indentation and Completion
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.project.DumbAware
import com.intellij.codeInsight.completion.CompletionService
import com.intellij.codeInsight.completion.CompletionProgressIndicator
import liveplugin.*
class TabIndentOrCompleteAction: AnAction(), DumbAware {
val actionManager = ActionManager.getInstance()
fun runAction(event: AnActionEvent, name: String) {
val dataContext = event.dataContext
val inputEvent = event.inputEvent
val action = actionManager.getAction(name)
if (action != null) {
val actionEvent = AnActionEvent.createFromAnAction(action, inputEvent, event.place, dataContext)
action.actionPerformed(actionEvent)
}
}
fun indent(event: AnActionEvent) {
runAction(event, "AutoIndentLines")
}
fun emacsIndent(event: AnActionEvent) {
runAction(event, "EmacsStyleIndent")
}
fun complete(event: AnActionEvent) {
runAction(event, "CodeCompletion")
}
fun gotoLineStart(event: AnActionEvent) {
runAction(event, "EditorLineStart")
}
fun insertTab(event: AnActionEvent) {
runAction(event, "EditorTab")
}
fun escape(event: AnActionEvent) {
runAction(event, "EditorEscape")
}
override fun actionPerformed(event: AnActionEvent) = {
val editor = event.getData(CommonDataKeys.EDITOR);
val caret = editor!!.caretModel!!.getPrimaryCaret();
val positionStart = caret!!.visualPosition;
val startColumn = positionStart!!.column;
val selectionModel = editor.selectionModel
if (selectionModel.hasSelection()) {
// - if there is an active selection, auto indent the region and
// then discard the active selection
show("Indenting region...")
indent(event)
escape(event)
} else {
// - if there is no active selection we'll try to indent and complete
// - first gotoLineStart is like pressing Home, it will move caret to the
// beginning of the current indentation, or the start of the line otherwise
gotoLineStart(event)
val positionAfterLineStartAction = caret!!.visualPosition;
val afterLineStartColumn = positionAfterLineStartAction!!.column;
// - if gotoLineStart moved the caret to the beginning of the line, use it
// again to move it back to the indentation
if (afterLineStartColumn == 0) {
gotoLineStart(event)
}
// - emacsIndent should indent according to the current language, or if the
// indentation is already correct or the language is not supported then it
// does nothing
emacsIndent(event)
val positionAfterIndentAction = caret!!.visualPosition;
val afterIndentColumn = positionAfterIndentAction!!.column;
// - if we're still at the same position after emacsIndent, then it did nothing
// and we can try to complete
if (afterIndentColumn == afterLineStartColumn && startColumn != 0) {
// - to complete we move caret back where we started and then complete
editor!!.caretModel!!.moveToVisualPosition(positionStart)
complete(event)
}
// - if we're at the startColumn after the emacsIndent then I just assume it
// is an empty line and insertTab
if (afterIndentColumn == startColumn) {
val completionService = CompletionService.getCompletionService()
val completionIndicator = completionService.currentCompletion as? CompletionProgressIndicator
if (completionIndicator == null) {
insertTab(event)
// - emacsIndent after insertTab magically fixes wrongly inserted tabs if the current
// file can be automatically indented, and if not then we keep the tab
emacsIndent(event)
}
}
}
}()
}
registerAction(id = "TabIndentOrComplete", keyStroke = "TAB", action = TabIndentOrCompleteAction())
if (!isIdeStartup) show("Enabled TabIndentOrComplete")
@rakete
Copy link
Author

rakete commented Jan 5, 2025

First time doing kotlin and anything with the jetbrains plugin api, so this probably sucks somehow. But it works well enough for myself for now.

Needs this: https://github.com/dkandalov/live-plugin

Oh, and also uses EmacsStyleIndent, so maybe just remove that if you don't want that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment