Skip to content

Commit 4f49f8e

Browse files
committed
feat: show git change markers on tabs
1 parent 35cc17b commit 4f49f8e

File tree

4 files changed

+155
-7
lines changed

4 files changed

+155
-7
lines changed

src/extensions/default/Git/main.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ define(function (require, exports, module) {
2727
"src/History",
2828
"src/NoRepo",
2929
"src/ProjectTreeMarks",
30-
"src/Remotes"
30+
"src/Remotes",
31+
"src/TabBarIntegration"
3132
];
3233
require(modules);
3334

@@ -48,10 +49,12 @@ define(function (require, exports, module) {
4849

4950
// export API's for other extensions
5051
if (typeof window === "object") {
52+
const TabBarIntegration = require("src/TabBarIntegration");
5153
window.phoenixGitEvents = {
5254
EventEmitter: EventEmitter,
5355
Events: Events,
54-
Git
56+
Git,
57+
TabBarIntegration
5558
};
5659
}
5760
});
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
define(function (require) {
2+
const EventEmitter = require("src/EventEmitter");
3+
const Events = require("src/Events");
4+
const Git = require("src/git/Git");
5+
const Preferences = require("src/Preferences");
6+
7+
// the cache of file statuses by path
8+
let fileStatusCache = {};
9+
10+
/**
11+
* this function is responsible to get the Git status for a file path
12+
*
13+
* @param {string} fullPath - the file path
14+
* @returns {Array|null} - Array of status strings or null if no status
15+
*/
16+
function getFileStatus(fullPath) {
17+
return fileStatusCache[fullPath] || null;
18+
}
19+
20+
/**
21+
* whether the file is modified or not
22+
*
23+
* @param {string} fullPath - the file path
24+
* @returns {boolean} - True if the file is modified otherwise false
25+
*/
26+
function isModified(fullPath) {
27+
const status = getFileStatus(fullPath);
28+
if (!status) {
29+
return false;
30+
}
31+
return (
32+
status.some(
33+
(statusType) =>
34+
statusType === Git.FILE_STATUS.MODIFIED ||
35+
statusType === Git.FILE_STATUS.RENAMED ||
36+
statusType === Git.FILE_STATUS.COPIED
37+
) && !status.includes(Git.FILE_STATUS.STAGED)
38+
);
39+
}
40+
41+
/**
42+
* whether the file is untracked or not
43+
*
44+
* @param {string} fullPath - the file path
45+
* @returns {boolean} - True if the file is untracked otherwise false
46+
*/
47+
function isUntracked(fullPath) {
48+
const status = getFileStatus(fullPath);
49+
if (!status) {
50+
return false;
51+
}
52+
53+
return status.includes(Git.FILE_STATUS.UNTRACKED);
54+
}
55+
56+
57+
// Update file status cache when Git status results are received
58+
EventEmitter.on(Events.GIT_STATUS_RESULTS, function (files) {
59+
// reset the cache
60+
fileStatusCache = {};
61+
62+
const gitRoot = Preferences.get("currentGitRoot");
63+
if (!gitRoot) {
64+
return;
65+
}
66+
67+
// we need to update cache with new status results
68+
files.forEach(function (entry) {
69+
const fullPath = gitRoot + entry.file;
70+
fileStatusCache[fullPath] = entry.status;
71+
});
72+
73+
// notify that file statuses have been updated
74+
EventEmitter.emit("GIT_FILE_STATUS_CHANGED", fileStatusCache);
75+
});
76+
77+
// clear cache when Git is disabled
78+
EventEmitter.on(Events.GIT_DISABLED, function () {
79+
fileStatusCache = {};
80+
EventEmitter.emit("GIT_FILE_STATUS_CHANGED", fileStatusCache);
81+
});
82+
83+
return {
84+
getFileStatus: getFileStatus,
85+
isModified: isModified,
86+
isUntracked: isUntracked
87+
};
88+
});

src/extensionsIntegrated/TabBar/main.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,39 @@ define(function (require, exports, module) {
131131
const isDirty = Helper._isFileModified(FileSystem.getFileForPath(entry.path));
132132
const isPlaceholder = entry.isPlaceholder === true;
133133

134+
let gitStatus = ""; // this will be shown in the tooltip when a tab is hovered
135+
let gitStatusClass = ""; // for styling
136+
let gitStatusLetter = ""; // shown in the tab, either U or M
137+
138+
if (window.phoenixGitEvents && window.phoenixGitEvents.TabBarIntegration) {
139+
const TabBarIntegration = window.phoenixGitEvents.TabBarIntegration;
140+
141+
// find the Git status
142+
// if untracked we add the git-new class and U char
143+
// if modified we add the git-modified class and M char
144+
if (TabBarIntegration.isUntracked(entry.path)) {
145+
gitStatus = "Untracked";
146+
gitStatusClass = "git-new";
147+
gitStatusLetter = "U";
148+
} else if (TabBarIntegration.isModified(entry.path)) {
149+
gitStatus = "Modified";
150+
gitStatusClass = "git-modified";
151+
gitStatusLetter = "M";
152+
}
153+
}
154+
134155
// create tab with all the appropriate classes
135156
const $tab = $(
136157
`<div class="tab
137158
${isActive ? "active" : ""}
138159
${isDirty ? "dirty" : ""}
139-
${isPlaceholder ? "placeholder" : ""}"
160+
${isPlaceholder ? "placeholder" : ""}
161+
${gitStatusClass}"
140162
data-path="${entry.path}"
141-
title="${Phoenix.app.getDisplayPath(entry.path)}">
163+
title="${Phoenix.app.getDisplayPath(entry.path)}${gitStatus ? " (" + gitStatus + ")" : ""}">
142164
<div class="tab-icon"></div>
143165
<div class="tab-name"></div>
166+
${gitStatusLetter ? `<div class="tab-git-status">${gitStatusLetter}</div>` : ""}
144167
<div class="tab-close"><i class="fa-solid fa-times"></i></div>
145168
</div>`
146169
);
@@ -541,6 +564,12 @@ define(function (require, exports, module) {
541564
// For editor changes, update only the tabs.
542565
MainViewManager.on(MainViewManager.EVENT_CURRENT_FILE_CHANGE, debounceUpdateTabs);
543566

567+
// to listen for the Git status changes
568+
// make sure that the git extension is available
569+
if (window.phoenixGitEvents && window.phoenixGitEvents.EventEmitter) {
570+
window.phoenixGitEvents.EventEmitter.on("GIT_FILE_STATUS_CHANGED", debounceUpdateTabs);
571+
}
572+
544573
// For working set changes, update only the tabs.
545574
const events = [
546575
"workingSetAdd",

src/styles/Extn-TabBar.less

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@
137137
font-size: 0.7rem;
138138
opacity: 0.7;
139139
font-weight: normal;
140+
position: relative;
141+
top: 0.1rem;
140142
}
141143

142144
.tab.active {
@@ -195,10 +197,10 @@
195197
.tab::before {
196198
content: "";
197199
color: transparent;
198-
font-size: 1.6rem;
200+
font-size: 1.5rem;
199201
position: absolute;
200202
left: 0.4rem;
201-
top: 0.25rem;
203+
top: 0.33rem;
202204
}
203205

204206
.tab.dirty::before {
@@ -215,7 +217,7 @@
215217

216218
.tab-close {
217219
font-size: 0.75rem;
218-
padding: 0.08rem 0.4rem;
220+
padding: 0.1rem 0.4rem;
219221
margin-left: 0.25rem;
220222
color: #666;
221223
transition: all 0.2s ease;
@@ -420,3 +422,29 @@
420422
background-color: transparent;
421423
pointer-events: all;
422424
}
425+
426+
/* ------ for the git status markers -------------- */
427+
.tab-git-status {
428+
font-size: 0.9rem;
429+
font-weight: bold;
430+
display: flex;
431+
align-items: center;
432+
justify-content: center;
433+
margin-left: 0.33rem;
434+
}
435+
436+
.git-modified .tab-git-status {
437+
color: #E3B551;
438+
}
439+
440+
.dark .git-modified .tab-git-status {
441+
color: #E3B551;
442+
}
443+
444+
.git-new .tab-git-status {
445+
color: #91CC41;
446+
}
447+
448+
.dark .git-new .tab-git-status {
449+
color: #91CC41;
450+
}

0 commit comments

Comments
 (0)