Subscribe

GitHub Universe

The event for people building the future of software, September 13-15 in San Francisco. Learn more

Updates to our Privacy Statement

When you check your GitHub account today, you’ll see an announcement letting you know that we’ve updated our Privacy Statement. Check it out!

Before we get into what’s new, rest assured that your information is still safe and sound. We have not made any substantive changes to the way we handle your information. For example, we still . . .

  • don’t sell your personal information to any third parties for their commercial purposes;
  • don’t have advertising on GitHub;
  • only require a small amount of information; and
  • only use your information for the purpose of providing services to you.

What’s changed

The new Privacy Statement clarifies quite a few things about how GitHub uses data, and how we permit third parties to use your information. For example, the new Privacy Statement describes how people such as researchers or archivists can use your public information on GitHub.com, and it explains that third parties using public information must respect our users’ choices. The new Privacy Statement now also includes a statement regarding tracking—we don’t track your web browsing off our site, and we don’t let third parties track you on GitHub.

Privacy Shield compliance

With this updated Privacy Statement, we’ve also applied for certification with the new EU–US Privacy Shield Framework. Privacy Shield is an agreement between the US Department of Commerce and the European Commission that provides companies in both the United States and the European Union a mechanism to comply with EU data protection requirements when transferring personal data from the EU to the US. We expect to receive our certification shortly, and we currently comply with the Privacy Shield Principles to protect all our users’ information.

Protecting our users

Although the Privacy Shield is only directed to users in the European Union, GitHub is committed to protecting all our users equally, regardless of where you live. So we are extending Privacy Shield’s benefits to all our users, including access to a free independent arbitration provider for privacy disputes.

Please take a few minutes to look over the new Privacy Statement, and please reach out if you have any questions.

Sponsors make Universe a better place

GitHub Universe September 2016

Gone are the days of booths and demos. GitHub Universe and Satellite sponsors help us make so much more possible. From food trucks and lounges to installations by local artists, our sponsors create beautiful, interactive experiences—not noise or sales pitches.

GitHub Universe—held September 13-15 in San Francisco—will bring together developers, open source advocates, systems and operations administrators, and entrepreneurs from across the GitHub community. This year’s sponsors are as varied as our crowd, and they’re helping us plan some special experiences that will provide something for everyone.

Octocat installation from Universe 2015

Interactive lounge from Universe 2015

Sponsorships touch all of GitHub Universe, including this year’s after party at the historic Masonic, The Big Bang. At The Big Bang, artist and actor COMMON will headline a benefit concert for Black Girls Code open to both conference attendees and the general public.

All of our sponsors have room to get creative at Pier 70 and the Masonic—and there's still time to make something magical happen. Get in touch this week or pick up tickets. You'll be in great company.

Thank you so much to our 2016 sponsors for joining our community and making Universe a better place.

sponsorlockup

OctoTales • MailChimp

What does a modern developer look like? The workforce is overcoming the stereotype of siloed, surly coders typing from dim, stall-like cubicles. We talked with the team at MailChimp, who are making great strides in embracing today’s developer — one who is, often, a person of many talents. Using GitHub for collaboration across teams, MailChimp is able to use the unique skills of all their employees to create a humming innovative environment.

“We’re an interesting company now, we really prize creativity, even down to the operations team. We’re encouraged to be creative in our solutions. We let anybody make a change to our code, and once it’s been peer reviewed, it gets deployed to production automatically,” said Bill O’Neill, Senior Software Engineer. “We can throw an idea out there, people can iterate over it, you can collaborate together even if you’re not in the same room.”

If you would like to be a part of the OctoTales series, tell us your story at tales@github.com

GitHub Pages now runs Jekyll 3.2

As promised, GitHub Pages has been upgraded to Jekyll 3.2. The move to Jekyll 3.2 brings over 100 improvements including the introduction of Gem-based themes.

You can begin using the Minima theme, starting today, by adding theme: minima to your site's config, a move which eliminates the need to manually copy templates or stylesheets into your repo. While Minima is the only theme supported today, we're working to make more themes available for use on GitHub Pages. Check out the GitHub help documentation for more information on adding a theme to your GitHub Pages site.

This should be a seamless transition for all GitHub Pages users, but if you have a particularly complex Jekyll site, we encourage you to get in touch with us. For more information on these changes, see the Jekyll changelog.

Back to School: Learn Fundamentals of Web Development with Thinkful

Thinkful is now offering their Fundamentals of Web Development course to Student Developer Pack members. As a student, you will receive two weeks of 1-on-1 mentorship from a professional software developer.

Thinkful joins the Student Developer Pack

Mentorship is at the core of a Thinkful education. Students who learn 1-on-1 with a mentor perform better than 98 percent of conventionally educated students. If you’re a computer science major looking to get industry-relevant web development skills, this program will help prepare you for the job search. Even if you’re not studying computer science, coding skills will give you a big edge in today’s job market.

The Fundamentals of Web Development curriculum will teach you:

  1. Structure and style with HTML and CSS
  2. Adding interactivity with jQuery
  3. Programming fundamentals in JavaScript
  4. AJAX and advanced jQuery

The Student Developer Pack gives students free access to the best developer tools from different technology companies like Stripe, Travis CI, and Unreal Engine. Now, with Thinkful included in the pack, it's even easier for students to start building great products.

Students, get your pack.

Publish Your Project Documentation with GitHub Pages

You might be familiar with how GitHub Pages helps you share your work with the world or maybe you have attended a class that helped you build your first GitHub Pages site. Recent improvements to GitHub Pages have made it easier to publish your site from a variety of sources. One of these sources is your repository's /docs folder.

Quality documentation is a hallmark of any healthy software project. For open-source projects, however, maintaining a robust compendium of knowledge detailing all the ins and outs is paramount. Well-curated documentation increases your project's approachability, provides asynchronous guidance, and fosters the type of uncoordinated collaboration that propels open-source software development.

Hosting your documentation on the web can present time-consuming challenges that make publishing and maintaining it an unrewarding experience — one that it's often easy to avoid. Grappling with multiple disparate publishing tools like FTP servers and databases means files often exist in various states and multiple locations, all of which require manual synchronization. To be clear, conventional web publishing provides unparalleled flexibility and power; but it comes at the expense of simplicity and, in many cases, utility.

When it comes to documentation, a path with less resistance is often the better approach.

GitHub Pages gives you a direct path to create websites for your projects, which makes it a natural choice for publishing and maintaining documentation. Because GitHub Pages supports Jekyll, you can pen your documentation in plain text or Markdown to help maintain a lower barrier to contribution. Jekyll also includes support for many helpful tools like variables, templates, and automatic code highlighting, which gives you much of the flexibility you'd find in a bulkier platform without the added complexity.

Most importantly, using GitHub Pages means your documentation lives alongside your code on GitHub, where you can use things like Issues and Pull Requests to ensure it receives the high level of care it deserves; and because GitHub Pages lets you publish from the /docs directory on the master branch, you can maintain your codebase and its published documentation on the same branch.

Get started today

Publishing your first documentation page only takes a few minutes.

  1. Create a /docs/index.md file on your repository's master branch.

  2. Add your content and any necessary Jekyll front matter, then commit your changes.

create-an-index-file

  1. Visit your repository's settings tab and select master branch /docs folder as the GitHub Pages source. Click save, and you're done.

set-the-publishing-source

GitHub Pages will read the contents of your /docs directory, convert the index.md into HTML, and publish the results at your GitHub Pages URL.

This will generate the most basic HTML output that you can further customize with templates, CSS, and other features available in Jekyll. To see examples of what all is possible, take a look at the GitHub Pages Showcase.

GitHub Extension for Visual Studio 2.0 is now available

We're pleased to announce that version 2.0 of the GitHub Extension for Visual Studio is now available. You can install it directly from the Tools and Extensions gallery in Visual Studio, as well as from our releases page and our website.

This release allows you to list your existing pull requests and create new ones directly from Visual Studio. You can also create gists directly from your code, making it even easier to share your code and collaborate. It also includes a slew of other enhancements and fixes, available in our release notes.

Screenshot of Visual Studio with GitHub pane open

As we focus our efforts on building workflows targeted towards maintainers and contributors, we'd love to hear your thoughts on how we can improve our tools. Let us know what works and what doesn't and how to make your coding and collaboration workflows easier.

Building your first Atom plugin

Authored by GitHub Campus Expert @NickTikhonov.

This tutorial will teach you how to write your first package for the Atom text editor. We'll be building a clone of Sourcerer, a plugin for finding and using code snippets from StackOverflow. By the end of this tutorial you will have written a plugin that converts programming problems written in English into code snippets pulled from StackOverflow:

Final Plugin Demo

What you need to know

Atom is written using web technologies. Our package will be built entirely using the EcmaScript 6 standard for JavaScript. You will need to be familiar with:

  • Using the command line
  • JavaScript programming
  • Promises
  • HTTP

Tutorial repository

You can follow this tutorial step-by-step or check out the supplementary repository on GitHub, which contains the plugin source code. The repository history contains one commit for each step outlined here.

Getting Started

Installing Atom

Download Atom by following the instructions on the Atom website. We will also need to install apm, the Atom Package Manager command line tool. You can do this by opening Atom and navigating to Atom > Install Shell Commands in the application menu. Check that apm was installed correctly by opening your command line terminal and running apm -v, which should print the version of the tool and related environments:

apm -v
> apm  1.9.2
> npm  2.13.3
> node 0.10.40
> python 2.7.10
> git 2.7.4

Generating starter code

Let's begin by creating a new package using a utility provided by Atom.

  • Launch the editor and press Cmd+Shift+P (on MacOS) or Ctrl+Shift+P (on Windows/Linux) to open the Command Palette.

  • Search for "Package Generator: Generate Package" and click the corresponding item on the list. You will see a prompt where you can enter the name of the package - "sourcefetch".

  • Press enter to generate the starter package, which should automatically be opened in Atom.

If you don't see package files appear in the sidebar, press Cmd+K Cmd+B (on MacOS) or Ctrl+K Ctrl+B (on Windows/Linux).

Generating a Package

The Command Palette lets you find and run package commands using fuzzy search. This is a convenient way to run commands without navigating menus or remembering shortcuts. We will be using it throughout this tutorial.

Running the starter code

Let's try out the starter package before diving into the code itself. We will first need to reload Atom to make it aware of the new package that was added. Open the Command Palette again and run the "Window: Reload" command.

Reloading the current window ensures that Atom runs the latest version of our source code. We will be running this command every time we want to test the changes we make to our package.

Run the package toggle command by navigating to Packages > sourcefetch > Toggle using the editor menu, or run sourcefetch: Toggle using the Command Palette. You should see a black box appear at the top of the screen. Hide it by running the command again.

Starter Package

The "toggle" command

Let's open lib/sourcefetch.js, which contains the package logic and defines the toggle command.

toggle() {
 console.log('Sourcefetch was toggled!');
 return (
   this.modalPanel.isVisible() ?
   this.modalPanel.hide() :
   this.modalPanel.show()
 );
}

toggle is a function exported by the module. It uses a ternary operator to call show and hide on the modal panel based on its visibility. modalPanel is an instance of Panel, a UI element provided by the Atom API. We declare modalPanel inside export default, which lets us access it as an instance variable with this.

this.subscriptions.add(atom.commands.add('atom-workspace', {
  'sourcefetch:toggle': () => this.toggle()
}));

The above statement tells Atom to execute toggle every time the user runs sourcefetch:toggle. We subscribe an anonymous function, () => this.toggle(), to be called every time the command is run. This is an example of event-driven programming, a common paradigm in JavaScript.

Atom Commands

Commands are nothing more than string identifiers for events triggered by the user, defined within a package namespace. We've already used:

  • package-generator:generate-package
  • window:reload
  • sourcefetch:toggle

Packages subscribe to commands in order to execute code in response to these events.

Making your first code change

Let's make our first code change—we're going to change toggle to reverse text selected by the user.

Change "toggle"

  • Change the toggle function to match the snippet below.
toggle() {
  let editor
  if (editor = atom.workspace.getActiveTextEditor()) {
    let selection = editor.getSelectedText()
    let reversed = selection.split('').reverse().join('')
    editor.insertText(reversed)
  }
}

Test your changes

  • Reload Atom by running Window: Reload in the Command Palette

  • Navigate to File > New to create a new file, type anything you like and select it with the cursor.

  • Run the sourcefetch:toggle command using the Command Palette, Atom menu, or by right clicking and selecting "Toggle sourcefetch"

The updated command will toggle the order of the selected text:

Reversing Selected Text

See all code changes for this step in the sourcefetch tutorial repository.

The Atom Editor API

The code we added uses the TextEditor API to access and manipulate the text inside the editor. Let's take a closer look.

let editor
if (editor = atom.workspace.getActiveTextEditor()) { /* ... */ }

The first two lines obtain a reference to a TextEditor instance. The variable assignment and following code is wrapped in a conditional to handle the case where there is no text editor instance available, for example, if the command was run while the user was in the settings menu.

let selection = editor.getSelectedText()

Calling getSelectedText gives us access to text selected by the user. If no text is currently selected, the function returns an empty string.

let reversed = selection.split('').reverse().join('')
editor.insertText(reversed)

Our selected text is reversed using JavaScript String methods . Finally, we call insertText to replace the selected text with the reversed counterpart. You can learn more about the different TextEditor methods available by reading the Atom API documentation.

Exploring the starter package

Now that we've made our first code change, let's take a closer look at how an Atom package is organized by exploring the starter code.

The main file

The main file is the entry-point to an Atom package. Atom knows where to find the main file from an entry in package.json:

"main": "./lib/sourcefetch",

The file exports an object with lifecycle functions which Atom calls on certain events.

  • activate is called when the package is initially loaded by Atom. This function is used to initialize objects such as user interface elements needed by the package, and to subscribe handler functions to package commands.

  • deactivate is called when the package is deactivated, for example, when the editor is closed or refreshed by the user.

  • serialize is called by Atom to allow you to save the state of the package between uses. The returned value is passed as an argument to activate when the package is next loaded by Atom.

We are going to rename our package command to fetch, and remove user interface elements we won't be using. Update the file to match the version below:

'use babel';

import { CompositeDisposable } from 'atom'

export default {

  subscriptions: null,

  activate() {
    this.subscriptions = new CompositeDisposable()

    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'sourcefetch:fetch': () => this.fetch()
    }))
  },

  deactivate() {
    this.subscriptions.dispose()
  },

  fetch() {
    let editor
    if (editor = atom.workspace.getActiveTextEditor()) {
      let selection = editor.getSelectedText()
      selection = selection.split('').reverse().join('')
      editor.insertText(selection)
    }
  }
};

Activation commands

To improve performance, Atom packages can be lazy loading. We can tell Atom to load our package only when certain commands are run by the user. These commands are called activation commands and are defined in package.json:

"activationCommands": {
  "atom-workspace": "sourcefetch:toggle"
},

Update this entry to make fetch an activation command.

"activationCommands": {
  "atom-workspace": "sourcefetch:fetch"
},

Some packages, such as those which modify Atom's appearance need to be loaded on startup. In those cases, activationCommands can be omitted entirely.

Triggering commands

Menu items

JSON files inside the menus folder specify which menu items are created for our package. Let's take a look at menus/sourcefetch.json:

"context-menu": {
  "atom-text-editor": [
    {
      "label": "Toggle sourcefetch",
      "command": "sourcefetch:toggle"
    }
  ]
},

The context-menu object lets us define new items in the right-click menu. Each item is defined by a label to be displayed in the menu and a command to run when the item is clicked.

"context-menu": {
  "atom-text-editor": [
    {
      "label": "Fetch code",
      "command": "sourcefetch:fetch"
    }
  ]
},

The menu object in the same file defines custom application menu items created for the package. We're going to rename this entry as well:

"menu": [
  {
    "label": "Packages",
    "submenu": [
      {
        "label": "sourcefetch",
        "submenu": [
          {
            "label": "Fetch code",
            "command": "sourcefetch:fetch"
          }
        ]
      }
    ]
  }
]

Keyboard shortcuts

Commands can also be triggered with keyboard shortcuts, defined with JSON files in the keymaps directory:

{
  "atom-workspace": {
    "ctrl-alt-o": "sourcefetch:toggle"
  }
}

The above lets package users call toggle with Ctrl+Alt+O on Windows/Linux or Cmd+Alt+O on MacOS.

Rename the referenced command to fetch:

"ctrl-alt-o": "sourcefetch:fetch"

Reload Atom by running the Window: Reload command. You should see that the application and right-click menus are updated, and the reverse functionality should work as before.

See all code changes for this step in the sourcefetch tutorial repository.

Using NodeJS modules

Now that we've made our first code change and learned about Atom package structure, let's introduce our first dependency—a module from Node Package Manager (npm). We will use the request module to make HTTP requests and download the HTML of a website. This functionality will be needed later, to scrape StackOverflow pages.

Installing dependencies

Open your command line application, navigate to your package root directory and run:

npm install --save request@2.73.0
apm install

These commands add the request Node module to our dependencies list and install the module into the node_modules directory. You should see a new entry in package.json. The @ symbol tells npm to install the specific version we will be using for this tutorial. Running apm install lets Atom know to use our newly installed module.

"dependencies": {
  "request": "^2.73.0"
}

Downloading and logging HTML to the Developer Console

Import request into our main file by adding an import statement to the top of lib/sourcefetch.js:

import { CompositeDisposable } from 'atom'
import request from 'request'

Now, add a new function, download to the module's exports, below fetch:

export default {  

  /* subscriptions, activate(), deactivate() */

  fetch() {
    ...
  },

  download(url) {
    request(url, (error, response, body) => {
      if (!error && response.statusCode == 200) {
        console.log(body)
      }
    })
  }
}

This function uses request to download the contents of a web page and logs the output to the Developer Console. When the HTTP request completes, our callback function will be called with the response as an argument.

The final step is to update fetch so that it calls download:

fetch() {
  let editor
  if (editor = atom.workspace.getActiveTextEditor()) {
    let selection = editor.getSelectedText()
    this.download(selection)
  }
},

Instead of reversing the selected text, fetch now treats the selection as a URL, passing it to download. Let's see our changes in action:

  • Reload Atom by running the Window: Reload command.

  • Open the Developer Tools. To do this, navigate to View > Developer > Toggle Developer Tools in the menu.

  • Create a new file, navigate to File > New.

  • Enter and select a URL, for example, http://www.atom.io.

  • Run our package command in any of the three ways previously described:

Downloading and logging HTML

Developer Tools make it easy to debug Atom packages. Any console.log statement will print to the interactive console, and you can use the Elements tab to explore the visual structure of the whole applicatio—which is just an HTML Document Object Model (DOM).

See all code changes for this step in the sourcefetch tutorial repository.

Using Promises to insert downloaded HTML into the editor

Ideally, we would like our download function to return the HTML as a string instead of just printing page contents into the console. Returning body won't work, however, since we get access to body inside of the callback rather than download itself.

We will solve this problem by returning a Promise rather than the value itself. Let's change download to return a Promise:

download(url) {
  return new Promise((resolve, reject) => {
    request(url, (error, response, body) => {
      if (!error && response.statusCode == 200) {
        resolve(body)
      } else {
        reject({
          reason: 'Unable to download page'
        })
      }
    })
  })
}

Promises allow us to return values obtained asynchronously by wrapping asynchronous logic in a function that provides two callbacks— resolve for returning a value successfully, and reject for notifying the caller of an error. We call reject if an error is returned by request, and resolve the HTML otherwise.

Let's change fetch to work with the Promise returned by download:

fetch() {
  let editor
  if (editor = atom.workspace.getActiveTextEditor()) {
    let selection = editor.getSelectedText()
    this.download(selection).then((html) => {
      editor.insertText(html)
    }).catch((error) => {
      atom.notifications.addWarning(error.reason)
    })
  }
},

In our new version of fetch, we get access to the HTML by calling then on the Promise returned by download. This lets us insert the HTML into the editor. We also accept and handle any errors returned by calling catch. We handle errors by displaying a warning notification using the Atom Notification API.

Let's see what changed. Reload Atom and run the package command on a selected URL:

Downloading and Inserting HTML

If the command is run on an invalid URL, a warning notification will be displayed:

Trying to Download an Invalid URL

See all code changes for this step in the sourcefetch tutorial repository.

Building a scraper to extract code snippets from StackOverflow HTML

The next step involves extracting code snippets from the HTML of a StackOverflow page we obtained in the previous step. In particular, we're interested in code from the accepted answer—an answer chosen to be correct by the question author. We can greatly simplify our package implementation by assuming any such answer to be relevant and correct.

Constructing a query using jQuery and Chrome Developer Tools

This section assumes you are using the Chrome web browser. You may be able to follow along using another browser, but instructions may change.

Let's take a look at a typical StackOverflow page that contains an accepted answer with a code snippet. We are going to explore the HTML using Chrome Developer Tools:

  • Open Chrome and navigate to any StackOverflow page containing an accepted answer with code, such as this hello world example in Python or this question about reading text from a file in C.

  • Scroll down to the accepted answer and highlight a section of the code snippet.

  • Right click and select Inspect

  • Inspect the location of the code snippet within the HTML code using the Elements browser.

Note that the document has the following structure:

<div class="accepted-answer">
  ...
    ...
      <pre>
        <code>
          ...snippet elements...
        </code>
      </pre>
    ...
  ...
</div>
  • The accepted answer is denoted by a div with class accepted-answer

  • Block code snippets are located inside a pre element

  • Elements that render the code snippet itself sit inside a code tag

Exploring the DOM using Chrome Developer Tools

Now let's construct a jQuery statement for extracting code snippets:

  • Click the Console tab within Developer Tools to access the JavaScript console.

  • Type $('div.accepted-answer pre code').text() into the console and press Enter.

You should see the accepted answer code snippets printed out in the console. The code we just ran uses a special $ function provided by jQuery. $ accepts a query string to select and return certain HTML elements from the website. Let's take a look at how this code works by considering a couple of intermediate example queries:

$('div.accepted-answer')
> [<div id="answer-1077349" class="answer accepted-answer" ... ></div>]

The above query will match all <div> elements that contain the class accepted-answer, in our case - just one div.

$('div.accepted-answer pre code')
> [<code>...</code>]

Building upon the previous, this query will match any <code> element that is inside a <pre> element contained within the previously matched <div>.

$('div.accepted-answer pre code').text()
> "print("Hello World!")"

The text function extracts and concatenates all text from the list of elements that would otherwise be returned by the previous query. This also strips out elements used for syntax highlighting purposes from the code.

Introducing Cheerio

Our next step involves using the query we created to implement a scraping function using Cheerio, a jQuery implementation for server-side applications.

Install Cheerio

  • Open your command line application, navigate to your package root directory and run:
npm install --save cheerio@0.20.0
apm install

Implement the scraping function

  • Add an import statement for cheerio in lib/sourcefetch.js:
import { CompositeDisposable } from 'atom'
import request from 'request'
import cheerio from 'cheerio'
  • Now create a new function that extracts code snippets given StackOverflow HTML, called scrape:
fetch() {
  ...
},

scrape(html) {
  $ = cheerio.load(html)
  return $('div.accepted-answer pre code').text()
},

download(url) {
  ...
}
  • Finally, let's change fetch to pass downloaded HTML to scrape instead of inserting it into the editor:
fetch() {
  let editor
  let self = this

  if (editor = atom.workspace.getActiveTextEditor()) {
    let selection = editor.getSelectedText()
    this.download(selection).then((html) => {
      let answer = self.scrape(html)
      if (answer === '') {
        atom.notifications.addWarning('No answer found :(')
      } else {
        editor.insertText(answer)
      }
    }).catch((error) => {
      console.log(error)
      atom.notifications.addWarning(error.reason)
    })
  }
},

Our scraping function is implemented in just two lines because cheerio does all of the work for us! We create a $ function by calling load with our HTML string, and use this function to run our jQuery statement and return the results. You can explore the entire Cheerio API in their developer documentation.

Testing the updated package

  • Reload Atom and run soucefetch:fetch on a selected StackOverflow URL to see the progress so far.

If we run the command on a page with an accepted answer, it will be inserted into the editor:

Scraping a StackOverflow Page

If we run the command on a page with no accepted answer, a warning notification will be displayed instead:

Running the command when there are no accepted answers

Our new iteration of fetch gives us the code snippet within a StackOverflow page instead of the entire HTML contents. Note that our updated fetch function checks for the absence of an answer and displays a notification to alert the user.

See all code changes for this step in the sourcefetch tutorial repository.

Implementing Google search to find relevant StackOverflow URLs

Now that we can turn StackOverflow URLs into code snippets, let's implement our final function, search, which will return a relevant URL given the description of a snippet, such as "hello world" or "quicksort". We will be using Google search via the unofficial google npm module, which allows us to search programmatically.

Installing the Google npm module

  • Install google by opening your command line application at the package root directory, and run:
npm install --save google@2.0.0
apm install

Importing and configuring the module

Add an import statement for google at the top of lib/sourcefetch.js:

import google from "google"

We will configure the library to limit the number of results returned during search. Add the following line below the import statement to limit returned results to just the top one.

google.resultsPerPage = 1

Implementing the search function

Next, let's implement our search function itself:

fetch() {
  ...
},

search(query, language) {
  return new Promise((resolve, reject) => {
    let searchString = `${query} in ${language} site:stackoverflow.com`

    google(searchString, (err, res) => {
      if (err) {
        reject({
          reason: 'A search error has occured :('
        })
      } else if (res.links.length === 0) {
        reject({
          reason: 'No results found :('
        })
      } else {
        resolve(res.links[0].href)
      }
    })
  })
},

scrape() {
  ...
}

The code above searches Google for a StackOverflow page relevant to the given query and programming language, returning the URL of the top result. Let's take a look at how it works:

let searchString = `${query} in ${language} site:stackoverflow.com`

We construct the search string using the query entered by the user and the current language selected. For example, if the user types "hello world" while editing Python, the query will be hello world in python site:stackoverflow.com. The final part of the string is a filter provided by Google Search that lets us limit results to those linked to StackOverflow.

google(searchString, (err, res) => {
  if (err) {
    reject({
      reason: 'A search error has occured :('
    })
  } else if (res.links.length === 0) {
    reject({
      reason: 'No results found :('
    })
  } else {
    resolve(res.links[0].href)
  }
})

We wrap the call to google inside a Promise so that we can return our URL asynchronously. We propagate any errors returned by the library, also returning an error when there are no results available. We resolve the URL of the top result otherwise.

Updating fetch to use search

Our final step is to update fetch to use search:

fetch() {
  let editor
  let self = this

  if (editor = atom.workspace.getActiveTextEditor()) {
    let query = editor.getSelectedText()
    let language = editor.getGrammar().name

    self.search(query, language).then((url) => {
      atom.notifications.addSuccess('Found google results!')
      return self.download(url)
    }).then((html) => {
      let answer = self.scrape(html)
      if (answer === '') {
        atom.notifications.addWarning('No answer found :(')
      } else {
        atom.notifications.addSuccess('Found snippet!')
        editor.insertText(answer)
      }
    }).catch((error) => {
      atom.notifications.addWarning(error.reason)
    })
  }
}

Let's take a look at what changed:

  • Our selected text is now treated as the query entered by the user.

  • We obtain the language of the current editor tab using the TextEditor API

  • We call search to obtain a URL, which we access by calling then on the resulting Promise

  • Instead of calling then on the Promise returned by download, we instead return the Promise itself and chain another then call onto the original call. This helps us avoid callback hell

See all code changes for this step in the sourcefetch tutorial repository.

Testing the final plugin

And we're done! See the final plugin in action by reloading Atom and running our package command on a problem description, and don't forget to select a language in the bottom-right corner.

Final Plugin Demo

Next steps

Now that you know the basics of hacking Atom, feel free to practice what you've learned by forking the sourcefetch repository and adding your own features.

The full schedule for GitHub Universe 2016 is now live

Join us in San Francisco for three days filled with the creativity and curiosity of the largest software community in the world. GitHub Universe features two keynotes, six featured speakers, and 24 breakout sessions that dive deep into the technical and team challenges of building software.

Together we’ll explore what it takes to define the future of software. Hear how individuals and teams are using modern tools, processes, and cultures to tackle the ever-expanding challenges we face. We’ll look at ways to apply that software to impact everything from communities to businesses. Check out the full schedule here.

Featured Speakers

Speakers at Universe 2016

GitHub Universe is all about developers, from best practices for workflows and techniques to an examination of the impact their work has on our world. The full program consists of advanced trainings, executive keynotes, two full days of content across three tracks, and a concert benefiting Black Girls Code.

Are you ready for launch?

Get Tickets

See the users you've blocked on your settings page

You've been able to block users for a while now, but you can now see who you've blocked from your account settings page. You can also search and add new users to your blocked list, as well as unblock any user that you've blocked by accident.

block-users

Of course, if a user is bothering you, you can always block them from their profile page directly.

block-users-profile

Check out the GitHub Help documentation for more information about blocking a user from your personal account or how to block a user from your organization.

Simpler GitHub Pages publishing

We're making it easier to publish a website with GitHub Pages. Now you can select a source in your repository settings and GitHub Pages will look for your content there.

Screenshot of Repository Settings - GitHub Pages - Source

  1. Selecting master branch will publish your site from the master branch. This is useful for repositories dedicated to website content.

  2. Selecting master branch /docs folder will publish from the /docs folder of your master branch. This lets you maintain documentation and code together on one branch, and open source maintainers can accept contributions for both in a single pull request.

Rest assured that existing project pages which use a gh-pages branch will keep working just like before, as will user and organization pages published from the master branch.

Check out the documentation to learn more.

Build an integration for GitHub

image

You can now access more tools from our Integrations Directory. The Directory now has more than 15 categories — from API management to application monitoring, there's a GitHub integration to support every stage of your development cycle.

We're inviting developers of all levels of expertise to create an integration that will help developers work better. If you've built a great integration for GitHub, we want to highlight it! Gitter, AppVeyor, and ZenHub did it, and so can you.

What we're looking for

Good software development relies on quality tools and developers today have a wide range of choices—language, framework, workflow, and environment among other factors. We're looking for tools that create a better overall development experience.

A couple of guidelines for Integrations Directory listings:

Helpful resources

To get listed, please follow the steps outlined in the listing requirements page.

You also should read our marketing guidelines and the existing Directory listings to get a better idea of how to put it all together. Please draft the content for your listing in a secret gist (markdown format) and email us at partnerships@github.com. If you have any questions, please don't hesitate to reach out to us at partnerships@github.com.

Back to school: Join the GitHub Education Community

Just in time for back-to-school preparations, we welcome you to join a new forum for technology education: education.github.community

GitHub Education Community

This supportive environment will allow educators to collaborate with each other, and with GitHubbers directly.

  • Discuss current trends in technology education with your peers from around the world. Share your opinions and successes.

  • Suggest improvements to GitHub Classroom, our new open source tool for managing your assignments on GitHub.

  • Let us know what you think of our upcoming lesson plans for teaching GitHub in the classroom, or share lesson plans of your own.

  • Discover new ways to utilize valuable tools from the GitHub Student Developer Pack in your curriculum.

New to GitHub? This is a great opportunity learn by interacting directly with teachers and learn from their experience. Community members are also invited to request a swag bag full of educational materials and goodies for your students.

Getting started is easy, head over and introduce yourself.

GitHub Universe Big Bang Benefit Concert

Big Bang After Party with COMMON

You've never been to an after party like this.

We’re doing something a little different with our conference after party and opening it up to GitHub communities outside of our conference attendees. The Big Bang brings together the community to support and celebrate an organization shaping the future of software, Black Girls Code. Featuring two extraordinary acts in the historic Masonic Auditorium, The Big Bang unites Universe attendees and the broader Bay Area community.

The concert is free for all Universe badge holders and open to the public for a $40 donation. All tickets are general admission seating.

Event Details

Wednesday, September 14 at The Masonic, 1111 California Street, San Francisco, CA.

Doors will open at 8pm. Light snacks will be served, and a cash bar will be available.

Headlining

COMMON

With his multi-Grammy Award winning music career, his continued accolades in film winning a 2015 Academy Award, 2015 Golden Globe Award, 2015 NAACP Award, 2015 Independent Spirit Award and 2015 Critic’s Choice Award, Common has emerged as one of Hollywood's most sought-after leading men. Whether inspiring audiences through music, movies, television, books, or his foundation, COMMON continues to break new ground and remains to be one of hip-hop’s most innovative, positive voices. We couldn’t have chosen a better headliner to represent GitHub and BGC for a great cause.

GitHub is extremely proud to partner with Black Girls Code

Black Girls CODE is devoted to showing the world that black girls can code, and do so much more. By reaching out to the community through workshops and after school programs, Black Girls CODE introduces computer coding lessons to young girls from underrepresented communities in programming languages such as Scratch or Ruby on Rails. Black Girls CODE's ultimate goal is to provide African-American youth with the skills to occupy some of the 1.4 million computing job openings expected to be available in the U.S. by 2020, and to train 1 million girls by 2040.

Let’s help them reach their goal!

Get Tickets >

Change the base branch of a Pull Request

You can now change the base branch of an open pull request. After you’ve created a pull request, you can modify the base branch so that the changes in the pull request are compared against a different branch. By changing the base branch of your original pull request rather than opening a new one with the correct base branch, you’ll be able to keep valuable work and discussion.

base-switching

Learn more in our help guide.