Skip to content

Commit

Permalink
Merge branch 'main' into release-vsce-1.19
Browse files Browse the repository at this point in the history
  • Loading branch information
icycodes committed Jan 27, 2025
2 parents 9093f54 + 8897506 commit d1b4af9
Show file tree
Hide file tree
Showing 13 changed files with 412 additions and 208 deletions.
4 changes: 2 additions & 2 deletions clients/eclipse/feature/feature.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<feature
id="com.tabbyml.features.tabby4eclipse"
label="Tabby"
version="0.0.2.31"
version="0.0.2.32"
provider-name="com.tabbyml">

<description url="http://www.example.com/description">
Expand All @@ -19,6 +19,6 @@

<plugin
id="com.tabbyml.tabby4eclipse"
version="0.0.2.31"/>
version="0.0.2.32"/>

</feature>
2 changes: 1 addition & 1 deletion clients/eclipse/plugin/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tabby Plugin for Eclipse
Bundle-SymbolicName: com.tabbyml.tabby4eclipse;singleton:=true
Bundle-Version: 0.0.2.31
Bundle-Version: 0.0.2.32
Bundle-Activator: com.tabbyml.tabby4eclipse.Activator
Bundle-Vendor: com.tabbyml
Require-Bundle: org.eclipse.ui,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public boolean isEqual(Version other, boolean ignorePatch) {
return this.patch == other.patch;
}

public boolean isZero() {
return this.major == 0 && this.minor == 0 && this.patch == 0;
}

private int parseInt(String str) {
try {
return Integer.parseInt(str);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public static String checkServerHealth(Map<String, Object> serverHealth) {
if (version != null) {
Version parsedVersion = new Version(version);
Version requiredVersion = new Version(MIN_SERVER_VERSION);
if (!parsedVersion.isGreaterOrEqualThan(requiredVersion)) {
if (!parsedVersion.isZero() && !parsedVersion.isGreaterOrEqualThan(requiredVersion)) {
return String.format(
"Tabby Chat requires Tabby server version %s or later. Your server is running version %s.",
MIN_SERVER_VERSION, version);
Expand Down Expand Up @@ -212,24 +212,25 @@ public static boolean openInEditor(FileLocation fileLocation) {
public static void openExternal(String url) {
Program.launch(url);
}

public static List<GitRepository> readGitRepositoriesInWorkspace() {
List<GitRepository> repositories = new ArrayList<>();
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject[] projects = workspaceRoot.getProjects();

for (IProject project : projects) {
try {
URI projectRootUri = project.getLocation().toFile().toURI();
com.tabbyml.tabby4eclipse.lsp.protocol.GitRepository repo = GitProvider.getInstance().getRepository(new GitRepositoryParams(projectRootUri.toString()));
if (repo != null) {
repositories.add(new GitRepository(repo.getRemoteUrl()));
}
} catch (Exception e) {
logger.warn("Error when read git repository.", e);
}
}
return repositories;
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject[] projects = workspaceRoot.getProjects();

for (IProject project : projects) {
try {
URI projectRootUri = project.getLocation().toFile().toURI();
com.tabbyml.tabby4eclipse.lsp.protocol.GitRepository repo = GitProvider.getInstance()
.getRepository(new GitRepositoryParams(projectRootUri.toString()));
if (repo != null) {
repositories.add(new GitRepository(repo.getRemoteUrl()));
}
} catch (Exception e) {
logger.warn("Error when read git repository.", e);
}
}
return repositories;
}

public static void setClipboardContent(String content) {
Expand Down Expand Up @@ -261,7 +262,8 @@ public static void applyContentInEditor(String content) {

public static Filepath fileUriToChatPanelFilepath(URI fileUri) {
String fileUriString = fileUri.toString();
com.tabbyml.tabby4eclipse.lsp.protocol.GitRepository gitRepo = GitProvider.getInstance().getRepository(new GitRepositoryParams(fileUriString));
com.tabbyml.tabby4eclipse.lsp.protocol.GitRepository gitRepo = GitProvider.getInstance()
.getRepository(new GitRepositoryParams(fileUriString));
String gitUrl = (gitRepo != null) ? gitRepo.getRemoteUrl() : null;
if (gitUrl != null) {
gitRemoteUrlToLocalRoot.put(gitUrl, gitRepo.getRoot());
Expand Down
5 changes: 2 additions & 3 deletions clients/vscode/src/chat/webview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
localUriToListFileItem,
escapeGlobPattern,
} from "./utils";
import { findFiles } from "../findFiles";
import mainHtml from "./html/main.html";
import errorHtml from "./html/error.html";

Expand Down Expand Up @@ -529,10 +530,8 @@ export class ChatWebview {
.join("");

const globPattern = `**/${caseInsensitivePattern}*`;

this.logger.info(`Searching files with pattern: ${globPattern}, limit: ${maxResults}`);

const files = await workspace.findFiles(globPattern, undefined, maxResults);
const files = await findFiles(globPattern, { maxResults });
this.logger.info(`Found ${files.length} files.`);
return files.map((uri) => localUriToListFileItem(uri, this.gitProvider));
} catch (error) {
Expand Down
183 changes: 183 additions & 0 deletions clients/vscode/src/findFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// We want to use the findFiles2 API, but it is still in the proposed state.
// Therefore, we implement a findFiles function here that can use the following rules in the exclude pattern:
// 1. .gitignore files
// 2. Settings from `files.exclude` and `search.exclude`
//
// See https://github.com/microsoft/vscode/blob/main/src/vscode-dts/vscode.proposed.findFiles2.d.ts

import { GlobPattern, RelativePattern, Uri, WorkspaceFolder, CancellationToken, workspace } from "vscode";
import path from "path";
import { getLogger } from "./logger";

const logger = getLogger("FindFiles");

// Map from workspace folder to gitignore patterns
const gitIgnorePatternsMap = new Map<string, Set<string>>();

function gitIgnoreItemToExcludePatterns(item: string, prefix?: string | undefined): string[] {
let pattern = item.trim();
if (pattern.length === 0) {
return [];
}
if (pattern.indexOf("/") === -1 || pattern.indexOf("/") === pattern.length - 1) {
if (!pattern.startsWith("**/")) {
pattern = `**/${pattern}`;
}
} else if (pattern.startsWith("/")) {
pattern = pattern.slice(1);
}
return [path.join(prefix ?? "", pattern), path.join(prefix ?? "", pattern, "/**")];
}

async function buildGitIgnorePatterns(workspaceFolder: WorkspaceFolder) {
const patterns = new Set<string>();
logger.debug(`Building gitignore patterns for workspace folder: ${workspaceFolder.uri.toString()}`);

// Read parent gitignore files
let current = workspaceFolder.uri;
let parent = current.with({ path: path.dirname(current.path) });
while (parent.path !== current.path) {
const gitignore = parent.with({ path: path.join(parent.path, ".gitignore") });
try {
const content = (await workspace.fs.readFile(gitignore)).toString();
content.split(/\r?\n/).forEach((line) => {
if (!line.trim().startsWith("#")) {
gitIgnoreItemToExcludePatterns(line).forEach((pattern) => patterns.add(pattern));
}
});
} catch (error) {
// ignore
}

// next
current = parent;
parent = current.with({ path: path.dirname(current.path) });
}

// Read subdirectories gitignore files
const ignoreFiles = await workspace.findFiles(new RelativePattern(workspaceFolder, "**/.gitignore"));
await Promise.all(
ignoreFiles.map(async (ignoreFile) => {
const prefix = path.relative(workspaceFolder.uri.path, path.dirname(ignoreFile.path));
try {
const content = (await workspace.fs.readFile(ignoreFile)).toString();
content.split(/\r?\n/).forEach((line) => {
if (!line.trim().startsWith("#")) {
gitIgnoreItemToExcludePatterns(line, prefix).forEach((pattern) => patterns.add(pattern));
}
});
} catch (error) {
// ignore
}
}),
);
// Update map
logger.debug(
`Completed building git ignore patterns for workspace folder: ${workspaceFolder.uri.toString()}, git ignore patterns: ${JSON.stringify([...patterns])}`,
);
gitIgnorePatternsMap.set(workspaceFolder.uri.toString(), patterns);
}

workspace.onDidChangeTextDocument(async (event) => {
const uri = event.document.uri;
if (path.basename(uri.fsPath) === ".gitignore") {
const workspaceFolder = workspace.getWorkspaceFolder(uri);
if (workspaceFolder) {
await buildGitIgnorePatterns(workspaceFolder);
}
}
});

export async function findFiles(
pattern: GlobPattern,
options?: {
excludes?: string[];
noUserSettings?: boolean; // User settings is used by default, set to true to skip user settings
noIgnoreFiles?: boolean; // .gitignore files are used by default, set to true to skip .gitignore files
maxResults?: number;
token?: CancellationToken;
},
): Promise<Uri[]> {
const combinedExcludes = new Set<string>();
if (options?.excludes) {
for (const exclude of options.excludes) {
combinedExcludes.add(exclude);
}
}
if (!options?.noUserSettings) {
const searchExclude: Record<string, boolean> =
(await workspace.getConfiguration("search", null).get("exclude")) ?? {};
const filesExclude: Record<string, boolean> =
(await workspace.getConfiguration("files", null).get("exclude")) ?? {};
for (const pattern in { ...searchExclude, ...filesExclude }) {
if (filesExclude[pattern]) {
combinedExcludes.add(pattern);
}
}
}
if (options?.noIgnoreFiles) {
const excludesPattern = `{${[...combinedExcludes].join(",")}}`;
logger.debug(
`Executing search: ${JSON.stringify({ includePattern: pattern, excludesPattern, maxResults: options?.maxResults, token: options?.token })}`,
);
return await workspace.findFiles(pattern, excludesPattern, options.maxResults, options.token);
} else {
await Promise.all(
workspace.workspaceFolders?.map(async (workspaceFolder) => {
if (!gitIgnorePatternsMap.has(workspaceFolder.uri.toString())) {
await buildGitIgnorePatterns(workspaceFolder);
}
}) ?? [],
);

return new Promise((resolve, reject) => {
if (options?.token) {
options?.token.onCancellationRequested((reason) => {
reject(reason);
});
}

const allSearches =
workspace.workspaceFolders?.map(async (workspaceFolder) => {
let includePattern: RelativePattern;
if (typeof pattern === "string") {
includePattern = new RelativePattern(workspaceFolder, pattern);
} else {
if (pattern.baseUri.toString().startsWith(workspaceFolder.uri.toString())) {
includePattern = pattern;
} else {
return [];
}
}
const allExcludes = new Set([
...combinedExcludes,
...(gitIgnorePatternsMap.get(workspaceFolder.uri.toString()) ?? []),
]);
const excludesPattern = `{${[...allExcludes].slice(0, 1000).join(",")}}`; // Limit to 1000 patterns
logger.debug(
`Executing search: ${JSON.stringify({ includePattern, excludesPattern, maxResults: options?.maxResults, token: options?.token })}`,
);
return await workspace.findFiles(includePattern, excludesPattern, options?.maxResults, options?.token);
}) ?? [];

const results: Uri[] = [];
Promise.all(
allSearches.map(async (search) => {
try {
const result = await search;
if (result.length > 0) {
results.push(...result);
if (options?.maxResults && results.length >= options.maxResults) {
resolve(results.slice(0, options.maxResults));
}
}
} catch (error) {
// ignore
}
}),
).then(() => {
resolve(results);
});
});
}
}
5 changes: 4 additions & 1 deletion crates/tabby/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ fn main() -> Result<(), Box<dyn Error>> {
EmitBuilder::builder()
.all_build()
.all_git()
.git_describe(false, true, Some("v*"))
// TODO(kweizh): we encounter a issue with match_pattern in vergen on Windows
// will add the match_pattern back when the issue is resolved
// ref: https://github.com/rustyhorde/vergen/issues/402
.git_describe(false, true, None)
.emit()?;
Ok(())
}
Loading

0 comments on commit d1b4af9

Please sign in to comment.