Skip to content

Commit

Permalink
Cache fixes, add more extensions as "c++"
Browse files Browse the repository at this point in the history
  • Loading branch information
aarcangeli committed Nov 2, 2024
1 parent 41b9929 commit d42a933
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 28 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# idea-clang-format Changelog

[Unreleased]

## [1.0.0] - 2024-11-02

### Added
Expand All @@ -11,4 +13,5 @@
- Settings page to configure `clang-format` binary path
- Format on save option

[Unreleased]: https://github.com/aarcangeli/idea-clang-format/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/aarcangeli/idea-clang-format/commits/v1.0.0
44 changes: 30 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,21 @@
[![Rating](https://img.shields.io/jetbrains/plugin/r/rating/20785-clang-format-tools)](https://plugins.jetbrains.com/plugin/20785-clang-format-tools)
[![Static Badge](https://img.shields.io/badge/Get%20from%20Marketplace-blue)](https://plugins.jetbrains.com/plugin/20785-clang-format-tools)

<img width="710" height="450" src="https://plugins.jetbrains.com/files/20785/screenshot_925c0be8-97a9-4912-bd75-eb10f06cc15b" alt="demo">
<img width="710" height="450" src="https://plugins.jetbrains.com/files/20785/screenshot_0edd8132-28b5-47a4-a819-03ae2230f2bd" alt="demo">

<!-- Plugin description -->

[Clang-Format Tools](https://plugins.jetbrains.com/plugin/20785-clang-format-tools/edit) adds language support for
`.clang-format` [option files](https://clang.llvm.org/docs/ClangFormatStyleOptions.html) and allow developers to format their code using
`clang-format` directly from the IDE.
`.clang-format` [option files](https://clang.llvm.org/docs/ClangFormatStyleOptions.html) and allow to format code directly from the IDE.

## Features
## Overview

- `.clang-format` file support with completion and documentation
- Format code using clang-format
- [Format code](#usage) using clang-format
- Automatically detects indentation style and column limit from `.clang-format` file (note that .editorconfig has the precedence)
- Automatically format code on save (via project settings)

At the moment, it is not possible to format only a selection of the code.
- Automatically format code on save (via [project settings](#format-on-save))
- Support all JetBrains IDEs (Rider, IntelliJ IDEA, PyCharm, WebStorm, etc.)
- Support any language supported by `clang-format` (C, C++, Java, JavaScript, TypeScript, etc.)

## Installation of clang-format

Expand All @@ -48,15 +47,27 @@ clang-format --version
Note: if `clang-format` is executed without arguments, it formats the code from standard input
and writes the result to the standard output.

## Format on save
## Usage

To format the code, you can use the `Code | Reformat Code`, this is the standard action used in JetBrains IDEs to format the code.

There are also alternative ways to format the code:

- Select a directory or a file in the project view, right-click, and choose `Reformat Code` (or use the shortcut) to format all files in the directory.
- In the Commit Tab, you can enable "Reformat code" to format the code before committing it.
- Format on save (see [Format on save](#format-on-save))

Note: At the moment, it is not possible to format only a selection of the code.

### Format on save

To enable the format on save feature, follow these steps:

1. Go to `File | Settings | Tools | Actions on Save`
2. Enable the `Reformat code` action
3. Optionally, choose the file types you want to format on save

## Configuration
### Configuration

This plugin should work out of the box, but if you want to customize the behavior, you can do so in the settings.

Expand All @@ -67,12 +78,17 @@ All configuration are stored at the application level, so they are shared across
- Enabled: Globally enable/disable formatting utilities (The language support remains active)
- Path: Allows you to specify a custom path to the `clang-format` executable, if it's not in your PATH

## Column limit
### Code style settings

This plugin will automatically detect the indentation style and column limit from the `.clang-format` file.

When the style `ColumnLimit` is set in the `.clang-format` file, the plugin will apply the settings to the editor.
- `ColumnLimit`: The IDE will show a vertical line at the specified column limit.
- `IndentWidth`: The number of spaces to add when "tab" is pressed, not used if `UseTab` is enabled.
- `TabWidth`: The size of the tab character in columns.
- `UseTab`: Indicates if the tab should be used for indentation instead of spaces. Only used when "tab" key is pressed.

Note: sometimes, the change in the column limit is not immediately visible in the editor.
You can force the update by enabling/disabling the integration (see Configuration) or by restarting the IDE.
Note: At the moment, options from `.editorconfig` files take precedence over `.clang-format` files.
Ensure that the `.editorconfig` file is correctly configured to avoid conflicts.

## General clang-format guide

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ class ClangFormatServiceImpl : ClangFormatService, Disposable {
val language = formatStyle["Language"]
val languageStr = language?.toString()?.trim()
if (languageStr == "Cpp") {
// for clang, Cpp is a fallback for any file.
// clang-format treat all unknown languages as C++
// we must ensure that the file is really c++
if (!ClangFormatCommons.isCppFile(file)) {
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import com.intellij.execution.configurations.PathEnvironmentVariableUtil
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.ModificationTracker
import com.intellij.openapi.vfs.AsyncFileListener
Expand All @@ -20,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.events.*
import com.intellij.psi.PsiFile
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
Expand Down Expand Up @@ -119,11 +122,10 @@ class ClangFormatStyleServiceImpl : ClangFormatStyleService, Disposable {

private fun saveUnsavedClangFormatFiles() {
// save changed documents
val unsavedClangFormats = ClangFormatCommons.getUnsavedClangFormats()
if (unsavedClangFormats.isNotEmpty()) {
if (ClangFormatCommons.getUnsavedClangFormats().isNotEmpty()) {
ApplicationManager.getApplication().invokeLater {
WriteAction.run<RuntimeException> {
for (document in unsavedClangFormats) {
for (document in ClangFormatCommons.getUnsavedClangFormats()) {
FileDocumentManager.getInstance().saveDocument(document)
}
}
Expand Down Expand Up @@ -229,17 +231,31 @@ class ClangFormatStyleServiceImpl : ClangFormatStyleService, Disposable {

private inner class CacheFileWatcher : AsyncFileListener {
override fun prepareChange(events: List<VFileEvent>): AsyncFileListener.ChangeApplier? {
if (isThereAnyChangeInClangFormat(events)) {
val hasSchemaChanged = isClangFormatFileChange(events)
val hasContentChanged = isThereClangFormatContentChange(events)
if (hasSchemaChanged || hasContentChanged) {
return object : AsyncFileListener.ChangeApplier {
override fun afterVfsChange() {
dropCaches()
if (hasSchemaChanged) {
dropCaches()
}
if (hasSchemaChanged || hasContentChanged) {
// The code style settings may have changed
for (project in service<ProjectManager>().openProjects) {
CodeStyleSettingsManager.getInstance(project).notifyCodeStyleSettingsChanged()
}
}
}
}
}
return null
}

private fun isThereAnyChangeInClangFormat(events: List<VFileEvent>): Boolean {
/**
* Return true if any .clang-format file is added or removed.
* Doesn't check if the content of the file has changed.
*/
private fun isClangFormatFileChange(events: List<VFileEvent>): Boolean {
for (event in events) {
if (event is VFileCreateEvent) {
if (ClangFormatCommons.isClangFormatFile(event.childName)) {
Expand Down Expand Up @@ -282,6 +298,20 @@ class ClangFormatStyleServiceImpl : ClangFormatStyleService, Disposable {
}
return false
}

/**
* Return true if any .clang-format has modified the content of the file.
*/
private fun isThereClangFormatContentChange(events: List<VFileEvent>): Boolean {
for (event in events) {
if (event is VFileContentChangeEvent) {
if (ClangFormatCommons.isClangFormatFile(event.file.name)) {
return true
}
}
}
return false
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,37 @@ package com.github.aarcangeli.ideaclangformat.utils
import com.github.aarcangeli.ideaclangformat.exceptions.ClangValidationError
import com.github.aarcangeli.ideaclangformat.exceptions.ClangFormatError
import com.github.aarcangeli.ideaclangformat.exceptions.ClangMissingLanguageException
import com.github.aarcangeli.ideaclangformat.services.ClangFormatService
import com.intellij.build.FileNavigatable
import com.intellij.build.FilePosition
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.ProcessOutput
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Document
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.testFramework.LightVirtualFile
import com.intellij.util.PathUtil
import org.jetbrains.annotations.NonNls
import java.io.File
import java.util.regex.Pattern

// TODO: We should respect the user's settings for the file extensions
val knownCppExtensions = setOf(
"c", "cp", "cpp", "cppm", "c++", "cxx", "cc", "cu",
"ino", "ixx",
"h", "hh", "hpp", "hxx", "inc", "inl", "ipp", "mpp", "pch", "tch", "tpp", "cuh",
)

object ClangFormatCommons {
private val CLANG_ERROR_PATTERN = Pattern.compile(
"(?<FileName>(?:[a-zA-Z]:|/)[^<>|?*:\\t]+):(?<LineNumber>\\d+):(?<Column>\\d+)\\s*:\\s*(?<Type>\\w+):\\s*(?<Message>.*)"
)

fun isCppFile(file: PsiFile): Boolean {
// TODO: add more extensions
val filename = file.name.lowercase()
return filename.endsWith(".cpp") || filename.endsWith(".h") || filename.endsWith(".h")
val extension = PathUtil.getFileExtension(file.name.lowercase()) ?: return false
return extension in knownCppExtensions
}

fun isUnconditionallyNotSupported(file: PsiFile): Boolean {
Expand Down Expand Up @@ -112,7 +116,7 @@ object ClangFormatCommons {
val documents = ArrayList<Document>()
for (document in FileDocumentManager.getInstance().unsavedDocuments) {
val file = FileDocumentManager.getInstance().getFile(document) ?: continue
if (ClangFormatCommons.isClangFormatFile(file.name) && document.isWritable) {
if (isClangFormatFile(file.name) && document.isWritable) {
documents.add(document)
}
}
Expand Down

0 comments on commit d42a933

Please sign in to comment.