Skip to content

Commit

Permalink
Merge pull request #1 from vlascik/regexes-for-tab-titles
Browse files Browse the repository at this point in the history
Regexes for tab titles
  • Loading branch information
vlascik authored May 29, 2024
2 parents 71bdf55 + 1eba4ca commit 0c510f3
Show file tree
Hide file tree
Showing 24 changed files with 259 additions and 85 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
/build
/out
/tabdir.jar
/tabdir redux.jar
/tabdir redux.zip
/build.properties
79 changes: 47 additions & 32 deletions META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,61 @@
~ limitations under the License.
-->

<idea-plugin url="https://github.com/crazyproger/Tabdir">
<id>ru.crazycoder.plugins.tabdir</id>
<name>Tabdir</name>
<idea-plugin url="https://github.com/vlascik/Tabdir">
<id>vlascik.plugins.tabdir-redux</id>
<name>Tabdir Redux</name>
<description><![CDATA[
<p>Awesome replacement for IDEA's 'Show directory in editor tabs for non-unique filenames'.
If you have same named files in different directories this plugin will allow you to have directory names
added to tab labels.
It only shows path differences so if you have "blog/controllers/index" and "news/controllers/index" it will
add either [blog] or [news].
In setting tab 'Tabdir' you can control how prefixes will be formatted.</p>
<p>Per-project configuration allows specifying different formatting rules for each directory in project.
To enable per-project configuration enable "<b>Use per project configuration</b>" checkbox(see screenshot
№3)
and reopen settings window(need close with Ok or Apply buttons). After this you will find 'Tabdir'
configuration
in project section(screenshot №4) in which different configurations can be specified. </p>
<h1 id="tabdir-redux">Tabdir Redux</h1>
<p>This plugin will allow you to have directory names added to tab labels, and use various ways to shorten them, including regex.</p>
<p>This is an updated and extended fork of IntelliJ plugin <strong>Tabdir</strong>, originally found on Github
at <a href="https://github.com/crazyproger/Tabdir">crazyproger/Tabdir</a> and IntelliJ Plugin
Marketplace <a href="https://plugins.intellij.net/plugin/?idea&amp;id=5045">here</a>.</p>
<p>You can install this fork of the plugin from the IntelliJ Marketplace <a href="https://plugins.jetbrains.com/plugin/24528-tabdir-redux">here</a>. You
will have to uninstall the old version first.</p>
<h2 id="description">Description</h2>
<p>Awesome replacement for IDEA&#39;s &#39;Show directory in editor tabs for non-unique filenames&#39;. This plugin will allow you to have directory names
added to tab labels. It can also show only path differences, so if you have <code>blog/controllers/index</code> and <code>news/controllers/index</code>, it will
add either <code>[blog]</code> or <code>[news]</code>. You can control how prefixes will be formatted in Settings tab &#39;Tabdir&#39;.</p>
<p>Tip: If you&#39;re having trouble managing dozens of open files, try enabling vertical tabs and &#39;Sort tabs alphabetically&#39;.</p>
<h2 id="features">Features</h2>
<ul>
<li>add file&#39;s path to tab title</li>
<li>shorten the file&#39;s path in table title, limit folder name length and number of nested folders</li>
<li>configure different tab prefixes for any folder</li>
<li>set empty path prefixes - if the file&#39;s path is shortened to an empty string, e.g. file is at top level of a folder, you can assign it a
custom prefix to keep these files ordered together</li>
<li>regex replacements for tab titles</li>
</ul>
<h2 id="configuration">Configuration</h2>
<p>Per-project configuration allows specifying different formatting rules for each directory in project.</p>
<p>To enable per-project configuration, enable &quot;Use per project configuration&quot; checkbox and reopen settings window (needs close with Ok or
Apply buttons). After this you will find &#39;Tabdir Project Settings&#39; configuration in &quot;Other Settings&quot;.</p>
<p>Note: you should turn off IDE Settings → Editor tabs → Show directory in editor tabs for non-unique filenames.</p>
Note: that you should turn off IDE Settings → Editor tabs → Show directory in editor tabs for non-unique filenames.
]]>
<p>As the original author seems to have lost access to publish new versions of the original plugin, with his permission, this is a fork under a new name.</p>
]]>
</description>

<version>1.7.0</version>
<vendor>Vladimir Rudev (mod: vl)</vendor>
<version>2.0.0</version>
<vendor>Vladimir Rudev, V. Lascik</vendor>
<idea-version since-build="233.13135.103" until-build="400.*"/>
<change-notes><![CDATA[
<ul>
<li>1.1 Add possibility to configure tab title format.</li>
<li>1.2 Fixed NPE for files with no path(in memory).</li>
<li>1.3 Add possibility to configure per project format for directories.</li>
<li>1.3.1 Share plugin to all IntelliJ Platform products.</li>
<li>1.4 Fixed NPE(was in phpstorm), update description.</li>
<li>1.5 Tabdir-4 issue fixed, copyright updated</li>
<li>1.6 Tabdir-9, Tabdir-10, Tabdir-11 issues fixed</li>
<li>1.6.1 Fix for IDEA 12</li>
<li>1.6.2 IDEA 12(EAP) release only: fixed Tabdir-15</li>
<li>1.6.3 Rebuild in java1.6</li>
<li>1.6.4 Fixed Tabdir-16</li>
<li>1.6.5 Merged Tabdir-16 into main</li>
<li>1.6.7 (mod: vl) Fixup for Idea 202+</li>
<li>2.0.0 Publish a fork under a new name (Tabdir Redux), add regex replacements for tab titles</li>
<li>1.7.0 (mod: vl) Fixup for Idea 233+ (2023.3.2+), add empty path prefixes</li>
<li>1.6.7 (mod: vl) Fixup for Idea 202+</li>
<li>1.6.5 Merged Tabdir-16 into main</li>
<li>1.6.4 Fixed Tabdir-16</li>
<li>1.6.3 Rebuild in java1.6</li>
<li>1.6.2 IDEA 12(EAP) release only: fixed Tabdir-15</li>
<li>1.6.1 Fix for IDEA 12</li>
<li>1.6 Tabdir-9, Tabdir-10, Tabdir-11 issues fixed</li>
<li>1.5 Tabdir-4 issue fixed, copyright updated</li>
<li>1.4 Fixed NPE(was in phpstorm), update description.</li>
<li>1.3.1 Share plugin to all IntelliJ Platform products.</li>
<li>1.3 Add possibility to configure per project format for directories.</li>
<li>1.2 Fixed NPE for files with no path(in memory).</li>
<li>1.1 Add possibility to configure tab title format.</li>
</ul>
]]>
</change-notes>
Expand Down
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,37 @@
Tabdir description and screenshots are [here](http://plugins.intellij.net/plugin/?idea&id=5045)
# Tabdir Redux

This plugin will allow you to have directory names added to tab labels, and use various ways to shorten them, including regex.

This is an updated and extended fork of IntelliJ plugin **Tabdir**, originally found at [crazyproger/Tabdir](https://github.com/crazyproger/Tabdir) and IntelliJ Plugin Marketplace [here](https://plugins.intellij.net/plugin/?idea&id=5045).

You can install this fork of the plugin from the IntelliJ Marketplace [here](https://plugins.jetbrains.com/plugin/24528-tabdir-redux). You will have to uninstall the old version first.

## Screenshots

![Screenshot1](docs/screenshot1.png "Screenshot1")

## Description

Awesome replacement for IDEA's 'Show directory in editor tabs for non-unique filenames'. This plugin will allow you to have directory names
added to tab labels. It can also show only path differences, so if you have `blog/controllers/index` and `news/controllers/index`, it will
add either `[blog]` or `[news]`. You can control how prefixes will be formatted in Settings tab 'Tabdir'.

Tip: If you're having trouble managing dozens of open files, try enabling vertical tabs and 'Sort tabs alphabetically'.

## Features

* add file's path to tab title
* shorten the file's path in table title, limit folder name length and number of nested folders
* configure different tab prefixes for any folder
* set empty path prefixes - if the file's path is shortened to an empty string, e.g. file is at top level of a folder, you can assign it a
custom prefix to keep these files ordered together
* regex replacements for tab titles

## Configuration

Per-project configuration allows specifying different formatting rules for each directory in project.

To enable per-project configuration, enable "Use per project configuration" checkbox and reopen settings window (needs close with Ok or
Apply buttons). After this you will find 'Tabdir Project Settings' configuration in "Other Settings".

Note: you should turn off IDE Settings → Editor tabs → Show directory in editor tabs for non-unique filenames.
File renamed without changes.
File renamed without changes.
4 changes: 3 additions & 1 deletion build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
<dirname property="basedir" file="${ant.file}"/>

<property file="build.properties"/>
<property name="version" value="2.0.0"/>

<!-- If the Idea and JDK on your system are not located at the paths below,
you can specify your paths in build.properties (example in build.properties.example).
If everything is fine as it is, the build.properties file may not exist,
Expand All @@ -35,7 +37,7 @@

<property name="lib.dir" value="lib"/>
<property name="build.dir" value="build"/>
<property name="tabdir.jar.file" value="${build.dir}/tabdir.jar"/>
<property name="tabdir.jar.file" value="${build.dir}/tabdir-redux-${version}.jar"/>

<path id="javac2.classpath">
<pathelement location="${javac2.home}/javac2.jar"/>
Expand Down
Binary file added docs/screenshot1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed lib/commons-lang3-3.13.0-javadoc.jar
Binary file not shown.
Binary file removed lib/commons-lang3-3.13.0-sources.jar
Binary file not shown.
Binary file removed lib/commons-lang3-3.13.0.jar
Binary file not shown.
Binary file added lib/commons-lang3-3.14.0-javadoc.jar
Binary file not shown.
Binary file added lib/commons-lang3-3.14.0-sources.jar
Binary file not shown.
Binary file added lib/commons-lang3-3.14.0.jar
Binary file not shown.
Binary file removed lib/commons-text-1.11.0-javadoc.jar
Binary file not shown.
Binary file removed lib/commons-text-1.11.0-sources.jar
Binary file not shown.
Binary file removed lib/commons-text-1.11.0.jar
Binary file not shown.
44 changes: 23 additions & 21 deletions src/ru/crazycoder/plugins/tabdir/SameFilenameTitleProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.ProjectScope;
import com.intellij.util.indexing.FileBasedIndex;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import ru.crazycoder.plugins.tabdir.configuration.FolderConfiguration;
import ru.crazycoder.plugins.tabdir.configuration.GlobalConfig;
import ru.crazycoder.plugins.tabdir.configuration.ProjectConfig;

import java.io.File;
import java.util.*;
import java.util.regex.Pattern;

/**
* User: crazycoder
Expand All @@ -46,7 +45,7 @@ public class SameFilenameTitleProvider

private final Logger log = Logger.getInstance(this.getClass().getCanonicalName());

private final GlobalConfig configuration = ApplicationManager.getApplication().getService(GlobalConfig.class);
private final GlobalConfig globalConfig = ApplicationManager.getApplication().getService(GlobalConfig.class);
private final Comparator<VirtualFile> comparator = Comparator.comparingInt(file -> file.getPath().length());


Expand All @@ -65,7 +64,7 @@ public String getEditorTabTitleInternal(final Project project, final VirtualFile
if (!needProcessFile(file, matchedConfiguration)) {
return null;
}
if (StringUtils.isNotEmpty(matchedConfiguration.getRelativeTo())) {
if (!matchedConfiguration.getRelativeTo().isEmpty()) {
return titleRelativeTo(file, matchedConfiguration);
} else {
return titleWithDiffs(project, file, matchedConfiguration);
Expand All @@ -77,8 +76,8 @@ public String getEditorTabTitleInternal(final Project project, final VirtualFile
}

private FolderConfiguration findConfiguration(final Project project, final VirtualFile file) {
if (!configuration.isProjectConfigEnabled()) {
return configuration;
if (!globalConfig.isProjectConfigEnabled()) {
return globalConfig;
}
ProjectConfig projectConfig = project.getService(ProjectConfig.class);
Map<String, FolderConfiguration> folderConfigs = projectConfig.getFolderConfigurations();
Expand All @@ -97,7 +96,7 @@ private FolderConfiguration findConfiguration(final Project project, final Virtu
}
if (matchedConfiguration == null) {
// no project config for current file - use global config
matchedConfiguration = configuration;
matchedConfiguration = globalConfig;
}
return matchedConfiguration;
}
Expand All @@ -106,21 +105,24 @@ private String titleRelativeTo(final VirtualFile file, final FolderConfiguration
String basePath = FileUtil.toSystemDependentName(configuration.getRelativeTo());
String filePath = FileUtil.toSystemDependentName(file.getPath());
String relativePath = FileUtil.getRelativePath(basePath, filePath, File.separatorChar);
String[] parts = StringUtils.split(relativePath, File.separatorChar);
if (parts != null) {
List<String> prefixes = new ArrayList<>(Arrays.asList(parts));
if (prefixes.size() > 1) {
prefixes.remove(prefixes.size() - 1);
return TitleFormatter.format(prefixes, file.getPresentableName(), configuration);
}
if (relativePath == null) relativePath = "";
String[] parts = relativePath.split(Pattern.quote(File.separator));
List<String> prefixes = new ArrayList<>(Arrays.asList(parts));
if (prefixes.size() > 1) {
prefixes.remove(prefixes.size() - 1);
return TitleFormatter.format(prefixes, file.getPresentableName(), configuration);
}
GlobalConfig globalConfig = ApplicationManager.getApplication().getService(GlobalConfig.class);
String globalEmptyPrefix = (globalConfig.getEmptyPathReplacement() != null) ? globalConfig.getEmptyPathReplacement() : "";
String emptyPrefix = (configuration.getEmptyPathReplacement() != null) ? configuration.getEmptyPathReplacement() : "";

String tabTitle = TitleFormatter.getRegexedTabTitle(file.getPresentableName());
return this.getEmptyPrefix(configuration) + tabTitle;
}

private String getEmptyPrefix(final FolderConfiguration folderConfig) {
String emptyPrefix = (folderConfig.getEmptyPathReplacement() != null) ? folderConfig.getEmptyPathReplacement() : "";
if (emptyPrefix.isBlank()) {
emptyPrefix = globalEmptyPrefix;
emptyPrefix = (globalConfig.getEmptyPathReplacement() != null) ? globalConfig.getEmptyPathReplacement() : "";
}
return emptyPrefix + file.getPresentableName();
return emptyPrefix;
}

private String titleWithDiffs(final Project project, final VirtualFile file, final FolderConfiguration configuration) {
Expand All @@ -146,8 +148,8 @@ private String titleWithDiffs(final Project project, final VirtualFile file, fin

/**
* @return <b>key</b> - ancestor folder name,<br/>
* <b>value</b> - neighbours of key folder that ancestors of similar files.<br/>
* Keys in map stored in order as in {@link #titleWithDiffs(com.intellij.openapi.project.Project, com.intellij.openapi.vfs.VirtualFile, ru.crazycoder.plugins.tabdir.configuration.FolderConfiguration)}
* <b>value</b> - neighbours of key folder that ancestors of similar files.<br/>
* Keys in map stored in order as in {@link #titleWithDiffs(com.intellij.openapi.project.Project, com.intellij.openapi.vfs.VirtualFile, ru.crazycoder.plugins.tabdir.configuration.FolderConfiguration)}
*/
private LinkedHashMap<String, Set<String>> calculatePrefixesWithoutDuplicates(VirtualFile file, Collection<VirtualFile> similarFiles) {
LinkedHashMap<String, Set<String>> prefixes = new LinkedHashMap<>();
Expand Down
73 changes: 63 additions & 10 deletions src/ru/crazycoder/plugins/tabdir/TitleFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@

package ru.crazycoder.plugins.tabdir;

import com.intellij.openapi.application.ApplicationManager;
import ru.crazycoder.plugins.tabdir.configuration.FolderConfiguration;
import ru.crazycoder.plugins.tabdir.configuration.GlobalConfig;

import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import static org.apache.commons.lang3.StringUtils.getCommonPrefix;
import static org.apache.commons.lang3.StringUtils.removeEnd;

/**
* User: crazycoder
Expand All @@ -32,11 +36,7 @@
public class TitleFormatter {

private static final int MIN_DUPLICATE_LENGTH = 3;

public static String format(LinkedHashMap<String, Set<String>> prefixes, String tabName, FolderConfiguration configuration) {
String joinedPrefixes = joinPrefixesWithRemoveDuplication(prefixes, configuration);
return MessageFormat.format(configuration.getTitleFormat(), joinedPrefixes, tabName);
}
private static final GlobalConfig globalConfig = ApplicationManager.getApplication().getService(GlobalConfig.class);

private static String joinPrefixesWithRemoveDuplication(LinkedHashMap<String, Set<String>> prefixes, FolderConfiguration configuration) {
List<String> keys = new LinkedList<>(prefixes.keySet());
Expand All @@ -46,7 +46,14 @@ private static String joinPrefixesWithRemoveDuplication(LinkedHashMap<String, Se
resultPrefixes.add(removeDuplicates(key, prefixes.get(key)));
}
StringBuilder buffer = join(resultPrefixes, configuration);
return removeEnd(buffer.toString(), configuration.getDirSeparator());
String bufferString = buffer.toString();
String dirSeparator = configuration.getDirSeparator();

if (bufferString.endsWith(dirSeparator)) {
return bufferString.substring(0, bufferString.length() - dirSeparator.length());
}

return bufferString;
}

/**
Expand Down Expand Up @@ -101,21 +108,67 @@ private static String removeDuplicates(final String key, Set<String> neighbours)
return result + suffix;
}

//-----simple format
public static String format(LinkedHashMap<String, Set<String>> prefixes, String tabName, FolderConfiguration configuration) {
String joinedPrefixes = joinPrefixesWithRemoveDuplication(prefixes, configuration);

String tabTitle = MessageFormat.format(configuration.getTitleFormat(), joinedPrefixes, tabName);

tabTitle = getRegexedTabTitle(tabTitle);

return tabTitle;
}

//-----simple format
public static String format(List<String> prefixes, String tabName, FolderConfiguration configuration) {
String joinedPrefixes = joinPrefixes(prefixes, configuration);
String tabTitle = MessageFormat.format(configuration.getTitleFormat(), joinedPrefixes, tabName);

tabTitle = getRegexedTabTitle(tabTitle);

if (joinedPrefixes.isBlank()) {
return configuration.getEmptyPathReplacement() + tabTitle;
tabTitle = configuration.getEmptyPathReplacement() + tabTitle;
}

return tabTitle;
}

// tabTitle regex replacements
public static String getRegexedTabTitle(String tabTitle) {
String[] filenameRegexes = globalConfig.getFilenameRegexes().split("\\R");

String result = tabTitle;
for (String regex : filenameRegexes) {
try {
if (!regex.contains("::")) continue;
String[] r = regex.split("[::]");
if (!(r.length == 3 || r.length == 1)) continue;
String match = r[0];
String replacement = r.length == 3 ? r[2] : "";
Pattern pattern = Pattern.compile(match, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(result);
result = matcher.replaceAll(replacement);
} catch (PatternSyntaxException ex) {
// Syntax error in the regular expression
} catch (IllegalArgumentException ex) {
// Syntax error in the replacement text (unescaped $ signs?)
} catch (IndexOutOfBoundsException ex) {
// Non-existent backreference used the replacement text
}
}
return result;
}

private static String joinPrefixes(List<String> prefixes, FolderConfiguration configuration) {
prefixes = getPrefixesSublist(prefixes, configuration);
StringBuilder buffer = join(prefixes, configuration);
return removeEnd(buffer.toString(), configuration.getDirSeparator());
String bufferString = buffer.toString();
String dirSeparator = configuration.getDirSeparator();

if (bufferString.endsWith(dirSeparator)) {
return bufferString.substring(0, bufferString.length() - dirSeparator.length());
}

return bufferString;
}

private static List<String> getPrefixesSublist(List<String> prefixes, FolderConfiguration configuration) {
Expand Down
Loading

0 comments on commit 0c510f3

Please sign in to comment.